home *** CD-ROM | disk | FTP | other *** search
/ Clickx 47 / Clickx 47.iso / assets / software / Miro_Installer.exe / Miro_Downloader.exe / app.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2008-01-10  |  87.9 KB  |  2,785 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.5)
  3.  
  4. import config
  5. import prefs
  6. import database
  7. db = database.defaultDatabase
  8. import views
  9. import indexes
  10. import sorts
  11. import maps
  12. import menu
  13. import util
  14. import feed
  15. import item
  16. import playlist
  17. import tabs
  18. import opml
  19. import folder
  20. import autodler
  21. import databaseupgrade
  22. import resources
  23. import selection
  24. import template
  25. import singleclick
  26. import storedatabase
  27. import subscription
  28. import downloader
  29. import autoupdate
  30. import xhtmltools
  31. import guide
  32. import idlenotifier
  33. import eventloop
  34. import searchengines
  35. import download_utils
  36. import os
  37. import re
  38. import shutil
  39. import cgi
  40. import traceback
  41. import threading
  42. import platform
  43. import dialogs
  44. import iconcache
  45. import moviedata
  46. import platformutils
  47. import logging
  48. import theme
  49. from string import Template
  50. import templatehelper
  51. import databasehelper
  52. import urllib
  53. import menubar
  54. from gtcache import gettext as _
  55. from gtcache import ngettext
  56. from clock import clock
  57. controller = None
  58. delegate = None
  59.  
  60. def main():
  61.     platformutils.setupLogging()
  62.     util.setupLogging()
  63.     Controller().Run()
  64.  
  65.  
  66. def start():
  67.     platformutils.setupLogging()
  68.     util.setupLogging()
  69.     Controller().runNonblocking()
  70.  
  71.  
  72. def startupFunction(func):
  73.     '''Decorator for startup functions.  If they throw an exception, miro will
  74.     show a error dialog and quit.
  75.     '''
  76.     
  77.     def wrapped(*args, **kwargs):
  78.         
  79.         try:
  80.             func(*args, **kwargs)
  81.         except:
  82.             util.failedExn('while finishing starting up')
  83.             frontend.exit(1)
  84.  
  85.  
  86.     return wrapped
  87.  
  88.  
  89. class PlaybackControllerBase:
  90.     
  91.     def __init__(self):
  92.         self.currentPlaylist = None
  93.         self.justPlayOne = False
  94.         self.currentItem = None
  95.         self.updateVideoTimeDC = None
  96.  
  97.     
  98.     def configure(self, view, firstItemId = None, justPlayOne = False):
  99.         self.currentPlaylist = Playlist(view, firstItemId)
  100.         self.justPlayOne = justPlayOne
  101.  
  102.     
  103.     def reset(self):
  104.         if self.currentPlaylist is not None:
  105.             eventloop.addIdle(self.currentPlaylist.reset, 'Reset Playlist')
  106.             self.currentPlaylist = None
  107.         
  108.  
  109.     
  110.     def configureWithSelection(self):
  111.         itemSelection = controller.selection.itemListSelection
  112.         view = itemSelection.currentView
  113.         if itemSelection.currentView is None:
  114.             return None
  115.         
  116.         for item in view:
  117.             itemid = item.getID()
  118.             if itemSelection.isSelected(view, itemid) and item.isDownloaded():
  119.                 self.configure(view, itemid)
  120.                 break
  121.                 continue
  122.         
  123.  
  124.     
  125.     def enterPlayback(self):
  126.         if self.currentPlaylist is None:
  127.             self.configureWithSelection()
  128.         
  129.         if self.currentPlaylist is not None:
  130.             startItem = self.currentPlaylist.cur()
  131.             if startItem is not None:
  132.                 self.playItem(startItem)
  133.             
  134.         
  135.  
  136.     
  137.     def exitPlayback(self, switchDisplay = True):
  138.         self.reset()
  139.         if switchDisplay:
  140.             controller.selection.displayCurrentTabContent()
  141.         
  142.  
  143.     
  144.     def playPause(self):
  145.         videoDisplay = controller.videoDisplay
  146.         frame = controller.frame
  147.         if frame.getDisplay(frame.mainDisplay) == videoDisplay:
  148.             videoDisplay.playPause()
  149.         else:
  150.             self.enterPlayback()
  151.  
  152.     
  153.     def pause(self):
  154.         videoDisplay = controller.videoDisplay
  155.         frame = controller.frame
  156.         if frame.getDisplay(frame.mainDisplay) == videoDisplay:
  157.             videoDisplay.pause()
  158.         
  159.  
  160.     
  161.     def removeItem(self, item):
  162.         if item.idExists():
  163.             item.executeExpire()
  164.         
  165.  
  166.     
  167.     def playItem(self, anItem):
  168.         
  169.         try:
  170.             if self.currentItem:
  171.                 self.currentItem.onViewedCancel()
  172.             
  173.             self.currentItem = None
  174.             while not os.path.exists(anItem.getVideoFilename()):
  175.                 logging.info("movie file '%s' is missing, skipping to next", anItem.getVideoFilename())
  176.                 eventloop.addIdle(self.removeItem, 'Remove deleted item', args = (anItem.item,))
  177.                 anItem = self.currentPlaylist.getNext()
  178.                 if anItem is None:
  179.                     self.stop()
  180.                     return None
  181.                     continue
  182.             self.currentItem = anItem
  183.             if anItem is not None:
  184.                 videoDisplay = controller.videoDisplay
  185.                 videoRenderer = videoDisplay.getRendererForItem(anItem)
  186.                 if videoRenderer is not None:
  187.                     self.playItemInternally(anItem, videoDisplay, videoRenderer)
  188.                 else:
  189.                     frame = controller.frame
  190.                     if frame.getDisplay(frame.mainDisplay) is videoDisplay:
  191.                         if videoDisplay.isFullScreen:
  192.                             videoDisplay.exitFullScreen()
  193.                         
  194.                         videoDisplay.stop()
  195.                     
  196.                     self.scheduleExternalPlayback(anItem)
  197.         except:
  198.             util.failedExn('when trying to play a video')
  199.             self.stop()
  200.  
  201.  
  202.     
  203.     def playItemInternally(self, anItem, videoDisplay, videoRenderer):
  204.         logging.info('Playing item with renderer: %s' % videoRenderer)
  205.         controller.videoDisplay.setExternal(False)
  206.         frame = controller.frame
  207.         if frame.getDisplay(frame.mainDisplay) is not videoDisplay:
  208.             frame.selectDisplay(videoDisplay, frame.mainDisplay)
  209.         
  210.         videoDisplay.selectItem(anItem, videoRenderer)
  211.         if config.get(prefs.RESUME_VIDEOS_MODE) and anItem.resumeTime > 10:
  212.             videoDisplay.playFromTime(anItem.resumeTime)
  213.         else:
  214.             videoDisplay.play()
  215.         self.startUpdateVideoTime()
  216.  
  217.     
  218.     def playItemExternally(self, itemID):
  219.         anItem = mapToPlaylistItem(db.getObjectByID(int(itemID)))
  220.         controller.videoInfoItem = anItem
  221.         newDisplay = TemplateDisplay('external-playback-continue', 'default')
  222.         frame = controller.frame
  223.         frame.selectDisplay(newDisplay, frame.mainDisplay)
  224.         return anItem
  225.  
  226.     
  227.     def scheduleExternalPlayback(self, anItem):
  228.         controller.videoDisplay.setExternal(True)
  229.         controller.videoDisplay.stopOnDeselect = False
  230.         controller.videoInfoItem = anItem
  231.         newDisplay = TemplateDisplay('external-playback', 'default')
  232.         frame = controller.frame
  233.         frame.selectDisplay(newDisplay, frame.mainDisplay)
  234.         anItem.markItemSeen()
  235.  
  236.     
  237.     def startUpdateVideoTime(self):
  238.         if not self.updateVideoTimeDC:
  239.             self.updateVideoTimeDC = eventloop.addTimeout(0.5, self.updateVideoTime, 'Update Video Time')
  240.         
  241.  
  242.     
  243.     def stopUpdateVideoTime(self):
  244.         if self.updateVideoTimeDC:
  245.             self.updateVideoTimeDC.cancel()
  246.             self.updateVideoTimeDC = None
  247.         
  248.  
  249.     
  250.     def updateVideoTime(self, repeat = True):
  251.         t = controller.videoDisplay.getCurrentTime()
  252.         if t != None and self.currentItem:
  253.             self.currentItem.setResumeTime(t)
  254.         
  255.         if repeat:
  256.             self.updateVideoTimeDC = eventloop.addTimeout(0.5, self.updateVideoTime, 'Update Video Time')
  257.         
  258.  
  259.     
  260.     def stop(self, switchDisplay = True, markAsViewed = False):
  261.         controller.videoDisplay.setExternal(False)
  262.         if self.updateVideoTimeDC:
  263.             self.updateVideoTime(repeat = False)
  264.             self.stopUpdateVideoTime()
  265.         
  266.         if self.currentItem:
  267.             self.currentItem.onViewedCancel()
  268.         
  269.         self.currentItem = None
  270.         frame = controller.frame
  271.         videoDisplay = controller.videoDisplay
  272.         if frame.getDisplay(frame.mainDisplay) == videoDisplay:
  273.             videoDisplay.stop()
  274.         
  275.         self.exitPlayback(switchDisplay)
  276.  
  277.     
  278.     def skip(self, direction, allowMovieReset = True):
  279.         frame = controller.frame
  280.         currentDisplay = frame.getDisplay(frame.mainDisplay)
  281.         if self.currentPlaylist is None:
  282.             self.stop()
  283.         elif allowMovieReset and direction == -1 and hasattr(currentDisplay, 'getCurrentTime') and currentDisplay.getCurrentTime() > 2:
  284.             currentDisplay.goToBeginningOfMovie()
  285.         elif config.get(prefs.SINGLE_VIDEO_PLAYBACK_MODE) or self.justPlayOne:
  286.             self.stop()
  287.         elif direction == 1:
  288.             nextItem = self.currentPlaylist.getNext()
  289.         else:
  290.             nextItem = self.currentPlaylist.getPrev()
  291.         if nextItem is None:
  292.             self.stop()
  293.         elif self.updateVideoTimeDC:
  294.             self.updateVideoTime(repeat = False)
  295.             self.stopUpdateVideoTime()
  296.         
  297.         self.playItem(nextItem)
  298.  
  299.     
  300.     def onMovieFinished(self):
  301.         self.stopUpdateVideoTime()
  302.         setToStart = False
  303.         if self.currentItem:
  304.             self.currentItem.setResumeTime(0)
  305.             if self.currentItem.getFeedURL() == 'dtv:singleFeed':
  306.                 setToStart = True
  307.             
  308.         
  309.         if setToStart:
  310.             frame = controller.frame
  311.             currentDisplay = frame.getDisplay(frame.mainDisplay)
  312.             currentDisplay.pause()
  313.             currentDisplay.goToBeginningOfMovie()
  314.             currentDisplay.pause()
  315.         else:
  316.             return self.skip(1, False)
  317.  
  318.  
  319.  
  320. class Display:
  321.     """Base class representing a display in a MainFrame's right-hand pane."""
  322.     
  323.     def __init__(self):
  324.         self.currentFrame = None
  325.  
  326.     
  327.     def isSelected(self):
  328.         return self.currentFrame is not None
  329.  
  330.     
  331.     def onSelected(self, frame):
  332.         '''Called when the Display is shown in the given MainFrame.'''
  333.         pass
  334.  
  335.     
  336.     def onDeselected(self, frame):
  337.         '''Called when the Display is no longer shown in the given
  338.         MainFrame. This function is called on the Display losing the
  339.         selection before onSelected is called on the Display gaining the
  340.         selection.'''
  341.         pass
  342.  
  343.     
  344.     def onSelected_private(self, frame):
  345.         if not self.currentFrame == None:
  346.             raise AssertionError
  347.         self.currentFrame = frame
  348.  
  349.     
  350.     def onDeselected_private(self, frame):
  351.         if not self.currentFrame == frame:
  352.             raise AssertionError
  353.         self.currentFrame = None
  354.  
  355.     
  356.     def callWhenReadyToDisplay(self, hook):
  357.         hook()
  358.  
  359.     
  360.     def cancel(self):
  361.         '''Called when the Display is not shown because it is not ready yet
  362.         and another display will take its place'''
  363.         pass
  364.  
  365.     
  366.     def getWatchable(self):
  367.         '''Subclasses can implement this if they can return a database view
  368.         of watchable items'''
  369.         return None
  370.  
  371.  
  372.  
  373. class VideoDisplayBase(Display):
  374.     
  375.     def __init__(self):
  376.         Display.__init__(self)
  377.         self.playbackController = None
  378.         self.volume = 1
  379.         self.previousVolume = 1
  380.         self.isPlaying = False
  381.         self.isPaused = False
  382.         self.isFullScreen = False
  383.         self.isExternal = False
  384.         self.stopOnDeselect = True
  385.         self.renderers = list()
  386.         self.activeRenderer = None
  387.  
  388.     
  389.     def initRenderers(self):
  390.         pass
  391.  
  392.     
  393.     def setExternal(self, external):
  394.         self.isExternal = external
  395.  
  396.     
  397.     def fillMovieData(self, filename, movie_data, callback):
  398.         for renderer in self.renderers:
  399.             success = renderer.fillMovieData(filename, movie_data)
  400.             if success:
  401.                 callback()
  402.                 return None
  403.                 continue
  404.         
  405.         callback()
  406.  
  407.     
  408.     def getRendererForItem(self, anItem):
  409.         for renderer in self.renderers:
  410.             if renderer.canPlayItem(anItem):
  411.                 return renderer
  412.                 continue
  413.         
  414.  
  415.     
  416.     def canPlayItem(self, anItem):
  417.         return self.getRendererForItem(anItem) is not None
  418.  
  419.     
  420.     def canPlayFile(self, filename):
  421.         for renderer in self.renderers:
  422.             if renderer.canPlayFile(filename):
  423.                 return True
  424.                 continue
  425.         
  426.         return False
  427.  
  428.     
  429.     def selectItem(self, anItem, renderer):
  430.         self.stopOnDeselect = True
  431.         controller.videoInfoItem = anItem
  432.         templ = TemplateDisplay('video-info', 'default')
  433.         area = controller.frame.videoInfoDisplay
  434.         controller.frame.selectDisplay(templ, area)
  435.         self.setActiveRenderer(renderer)
  436.         self.activeRenderer.selectItem(anItem)
  437.         self.activeRenderer.setVolume(self.getVolume())
  438.  
  439.     
  440.     def setActiveRenderer(self, renderer):
  441.         self.activeRenderer = renderer
  442.  
  443.     
  444.     def reset(self):
  445.         self.isPlaying = False
  446.         self.isPaused = False
  447.         self.stopOnDeselect = True
  448.         if self.activeRenderer is not None:
  449.             self.activeRenderer.reset()
  450.         
  451.         self.activeRenderer = None
  452.  
  453.     
  454.     def goToBeginningOfMovie(self):
  455.         if self.activeRenderer is not None:
  456.             self.activeRenderer.goToBeginningOfMovie()
  457.         
  458.  
  459.     
  460.     def playPause(self):
  461.         if self.isPlaying:
  462.             self.pause()
  463.         else:
  464.             self.play()
  465.  
  466.     
  467.     def playFromTime(self, startTime):
  468.         if self.activeRenderer is not None:
  469.             self.activeRenderer.playFromTime(startTime)
  470.         
  471.         self.isPlaying = True
  472.         self.isPaused = False
  473.  
  474.     
  475.     def play(self):
  476.         if self.activeRenderer is not None:
  477.             self.activeRenderer.play()
  478.         
  479.         self.isPlaying = True
  480.         self.isPaused = False
  481.  
  482.     
  483.     def pause(self):
  484.         if self.activeRenderer is not None:
  485.             self.activeRenderer.pause()
  486.         
  487.         self.isPlaying = False
  488.         self.isPaused = True
  489.  
  490.     
  491.     def stop(self):
  492.         if self.isFullScreen:
  493.             self.exitFullScreen()
  494.         
  495.         if self.activeRenderer is not None:
  496.             self.activeRenderer.stop()
  497.         
  498.         self.reset()
  499.  
  500.     
  501.     def goFullScreen(self):
  502.         self.isFullScreen = True
  503.         if not self.isPlaying:
  504.             self.play()
  505.         
  506.  
  507.     
  508.     def exitFullScreen(self):
  509.         self.isFullScreen = False
  510.  
  511.     
  512.     def getCurrentTime(self):
  513.         if self.activeRenderer is not None:
  514.             return self.activeRenderer.getCurrentTime()
  515.         
  516.  
  517.     
  518.     def setCurrentTime(self, seconds):
  519.         if self.activeRenderer is not None:
  520.             self.activeRenderer.setCurrentTime(seconds)
  521.         
  522.  
  523.     
  524.     def getProgress(self):
  525.         if self.activeRenderer is not None:
  526.             return self.activeRenderer.getProgress()
  527.         
  528.         return 0
  529.  
  530.     
  531.     def setProgress(self, progress):
  532.         if self.activeRenderer is not None:
  533.             return self.activeRenderer.setProgress(progress)
  534.         
  535.  
  536.     
  537.     def getDuration(self):
  538.         if self.activeRenderer is not None:
  539.             return self.activeRenderer.getDuration()
  540.         
  541.  
  542.     
  543.     def setVolume(self, level):
  544.         if level > 1:
  545.             level = 1
  546.         
  547.         if level < 0:
  548.             level = 0
  549.         
  550.         self.volume = level
  551.         config.set(prefs.VOLUME_LEVEL, level)
  552.         if self.activeRenderer is not None:
  553.             self.activeRenderer.setVolume(level)
  554.         
  555.  
  556.     
  557.     def getVolume(self):
  558.         return self.volume
  559.  
  560.     
  561.     def muteVolume(self):
  562.         self.previousVolume = self.getVolume()
  563.         self.setVolume(0)
  564.  
  565.     
  566.     def restoreVolume(self):
  567.         self.setVolume(self.previousVolume)
  568.  
  569.     
  570.     def onDeselected(self, frame):
  571.         if self.stopOnDeselect:
  572.             if self.isPlaying or self.isPaused:
  573.                 controller.playbackController.stop(False)
  574.             
  575.  
  576.  
  577.  
  578. class VideoRenderer:
  579.     
  580.     def __init__(self):
  581.         self.interactivelySeeking = False
  582.  
  583.     
  584.     def canPlayItem(self, anItem):
  585.         return self.canPlayFile(anItem.getVideoFilename())
  586.  
  587.     
  588.     def canPlayFile(self, filename):
  589.         return False
  590.  
  591.     
  592.     def fillMovieData(self, filename, movie_data):
  593.         return False
  594.  
  595.     
  596.     def getDisplayTime(self):
  597.         seconds = self.getCurrentTime()
  598.         return util.formatTimeForUser(seconds)
  599.  
  600.     
  601.     def getDisplayDuration(self):
  602.         seconds = self.getDuration()
  603.         return util.formatTimeForUser(seconds)
  604.  
  605.     
  606.     def getDisplayRemainingTime(self):
  607.         seconds = abs(self.getCurrentTime() - self.getDuration())
  608.         return util.formatTimeForUser(seconds, -1)
  609.  
  610.     
  611.     def getProgress(self):
  612.         duration = self.getDuration()
  613.         if duration == 0 or duration == None:
  614.             return 0
  615.         
  616.         return self.getCurrentTime() / duration
  617.  
  618.     
  619.     def setProgress(self, progress):
  620.         if progress > 1:
  621.             progress = 1
  622.         
  623.         if progress < 0:
  624.             progress = 0
  625.         
  626.         self.setCurrentTime(self.getDuration() * progress)
  627.  
  628.     
  629.     def selectItem(self, anItem):
  630.         self.selectFile(anItem.getVideoFilename())
  631.  
  632.     
  633.     def selectFile(self, filename):
  634.         pass
  635.  
  636.     
  637.     def reset(self):
  638.         pass
  639.  
  640.     
  641.     def setCurrentTime(self, seconds):
  642.         pass
  643.  
  644.     
  645.     def getDuration(self):
  646.         return 0
  647.  
  648.     
  649.     def setVolume(self, level):
  650.         pass
  651.  
  652.     
  653.     def goToBeginningOfMovie(self):
  654.         pass
  655.  
  656.     
  657.     def getCurrentTime(self):
  658.         pass
  659.  
  660.     
  661.     def playFromTime(self, position):
  662.         self.play()
  663.         self.setCurrentTime(position)
  664.  
  665.     
  666.     def play(self):
  667.         pass
  668.  
  669.     
  670.     def pause(self):
  671.         pass
  672.  
  673.     
  674.     def stop(self):
  675.         pass
  676.  
  677.     
  678.     def getRate(self):
  679.         return 1
  680.  
  681.     
  682.     def setRate(self, rate):
  683.         pass
  684.  
  685.     
  686.     def movieDataProgramInfo(self, videoPath, thumbnailPath):
  687.         raise NotImplementedError()
  688.  
  689.  
  690. import frontend
  691.  
  692. class Controller(frontend.Application):
  693.     
  694.     def __init__(self):
  695.         global controller, delegate
  696.         frontend.Application.__init__(self)
  697.         if not controller is None:
  698.             raise AssertionError
  699.         if not delegate is None:
  700.             raise AssertionError
  701.         controller = self
  702.         delegate = frontend.UIBackendDelegate()
  703.         self.frame = None
  704.         self.inQuit = False
  705.         self.guideURL = None
  706.         self.guide = None
  707.         self.initial_feeds = False
  708.         self.finishedStartup = False
  709.         self.idlingNotifier = None
  710.         self.gatheredVideos = None
  711.         self.sendingCrashReport = 0
  712.         self.librarySearchTerm = None
  713.         self.newVideosSearchTerm = None
  714.  
  715.     
  716.     def onStartup(self, gatheredVideos = None):
  717.         logging.info('Starting up %s', config.get(prefs.LONG_APP_NAME))
  718.         logging.info('Version:    %s', config.get(prefs.APP_VERSION))
  719.         logging.info('Revision:   %s', config.get(prefs.APP_REVISION))
  720.         logging.info('Builder:    %s', config.get(prefs.BUILD_MACHINE))
  721.         logging.info('Build Time: %s', config.get(prefs.BUILD_TIME))
  722.         util.print_mem_usage('Pre everything memory check')
  723.         logging.info('Loading preferences...')
  724.         config.load()
  725.         config.addChangeCallback(self.configDidChange)
  726.         feed.setDelegate(delegate)
  727.         feed.setSortFunc(sorts.item)
  728.         autoupdate.setDelegate(delegate)
  729.         database.setDelegate(delegate)
  730.         dialogs.setDelegate(delegate)
  731.         if not config.get(prefs.STARTUP_TASKS_DONE):
  732.             logging.info('Showing startup dialog...')
  733.             delegate.performStartupTasks(self.finishStartup)
  734.             config.set(prefs.STARTUP_TASKS_DONE, True)
  735.             config.save()
  736.         else:
  737.             self.finishStartup(gatheredVideos)
  738.         logging.info('Starting event loop thread')
  739.         eventloop.startup()
  740.  
  741.     
  742.     def finishStartup(self, gatheredVideos = None):
  743.         self.gatheredVideos = gatheredVideos
  744.         eventloop.addUrgentCall(self.initializeDatabase, 'Initializing database')
  745.  
  746.     
  747.     def initializeDatabase(self):
  748.         
  749.         try:
  750.             views.initialize()
  751.             util.print_mem_usage('Pre-database memory check:')
  752.             logging.info('Restoring database...')
  753.             database.defaultDatabase.liveStorage = storedatabase.LiveStorage()
  754.             db.recomputeFilters()
  755.             eventloop.addUrgentCall(self.checkMoviesDirectoryGone, 'checking movies directory')
  756.         except databaseupgrade.DatabaseTooNewError:
  757.             title = _('Database too new')
  758.             description = Template(_('You have a database that was saved with a newer version of $shortAppName. You must download the latest version of $shortAppName and run that.')).substitute(shortAppName = config.get(prefs.SHORT_APP_NAME))
  759.             
  760.             def callback(dialog):
  761.                 eventloop.quit()
  762.                 frontend.quit(True)
  763.  
  764.             dialogs.MessageBoxDialog(title, description).run(callback)
  765.  
  766.  
  767.     initializeDatabase = startupFunction(initializeDatabase)
  768.     
  769.     def checkMoviesDirectoryGone(self):
  770.         if not self.moviesDirectoryGone():
  771.             eventloop.addUrgentCall(self.finalizeStartup, 'finalizing startup')
  772.             return None
  773.         
  774.         title = _('Video Directory Missing')
  775.         description = _("\nMiro can't find your primary video directory.  This may be because it's located on an external drive that is currently disconnected.\n\nIf you continue, the video directory will be reset to a location on this drive (this will cause you to lose some details about the videos on the external drive).  You can also quit, connect the drive, and relaunch Miro.")
  776.         dialog = dialogs.ChoiceDialog(title, description, dialogs.BUTTON_QUIT, dialogs.BUTTON_LAUNCH_MIRO)
  777.         
  778.         def callback(dialog):
  779.             if dialog.choice == dialogs.BUTTON_LAUNCH_MIRO:
  780.                 eventloop.addUrgentCall(self.finalizeStartup, 'finalizing startup')
  781.             else:
  782.                 eventloop.quit()
  783.                 frontend.quit(True)
  784.  
  785.         dialog.run(callback)
  786.  
  787.     checkMoviesDirectoryGone = startupFunction(checkMoviesDirectoryGone)
  788.     
  789.     def finalizeStartup(self):
  790.         downloader.startupDownloader()
  791.         util.print_mem_usage('Post-downloader memory check')
  792.         self.setupGlobalFeed(u'dtv:manualFeed', initiallyAutoDownloadable = False)
  793.         self.setupGlobalFeed(u'dtv:singleFeed', initiallyAutoDownloadable = False)
  794.         self.setupGlobalFeed(u'dtv:search', initiallyAutoDownloadable = False)
  795.         self.setupGlobalFeed(u'dtv:searchDownloads')
  796.         tabs.reloadStaticTabs()
  797.         
  798.         try:
  799.             channelTabOrder = util.getSingletonDDBObject(views.channelTabOrder)
  800.         except LookupError:
  801.             logging.info('Creating channel tab order')
  802.             channelTabOrder = tabs.TabOrder(u'channel')
  803.  
  804.         
  805.         try:
  806.             playlistTabOrder = util.getSingletonDDBObject(views.playlistTabOrder)
  807.         except LookupError:
  808.             logging.info('Creating playlist tab order')
  809.             playlistTabOrder = tabs.TabOrder(u'playlist')
  810.  
  811.         searchengines.createEngines()
  812.         _getThemeHistory()
  813.         self.newTab = None
  814.         self.downloadTab = None
  815.         for tab in views.allTabs:
  816.             if tab.tabTemplateBase == 'newtab':
  817.                 self.newTab = tab
  818.                 continue
  819.             if tab.tabTemplateBase == 'downloadtab':
  820.                 self.downloadTab = tab
  821.                 continue
  822.         
  823.         views.unwatchedItems.addAddCallback(self.onUnwatchedItemsCountChange)
  824.         views.unwatchedItems.addRemoveCallback(self.onUnwatchedItemsCountChange)
  825.         views.downloadingItems.addAddCallback(self.onDownloadingItemsCountChange)
  826.         views.downloadingItems.addRemoveCallback(self.onDownloadingItemsCountChange)
  827.         self.onUnwatchedItemsCountChange(None, None)
  828.         self.onDownloadingItemsCountChange(None, None)
  829.         self.setupGlobalFeed(u'dtv:directoryfeed')
  830.         logging.info('Spawning auto downloader...')
  831.         autodler.startDownloader()
  832.         if config.get(prefs.LIMIT_UPSTREAM) is True:
  833.             logging.info('Spawning idle notifier')
  834.             self.idlingNotifier = idlenotifier.IdleNotifier(self)
  835.             self.idlingNotifier.start()
  836.         
  837.         self.playbackController = frontend.PlaybackController()
  838.         util.print_mem_usage('Pre-UI memory check')
  839.         logging.info('Displaying main frame...')
  840.         self.frame = frontend.MainFrame(self)
  841.         logging.info('Creating video display...')
  842.         self.videoDisplay = frontend.VideoDisplay()
  843.         self.videoDisplay.initRenderers()
  844.         self.videoDisplay.playbackController = self.playbackController
  845.         self.videoDisplay.setVolume(config.get(prefs.VOLUME_LEVEL))
  846.         util.print_mem_usage('Post-UI memory check')
  847.         self.selection = selection.SelectionHandler()
  848.         self.selection.selectFirstTab()
  849.         if self.initial_feeds:
  850.             views.feedTabs.resetCursor()
  851.             tab = views.feedTabs.getNext()
  852.             if tab is not None:
  853.                 self.selection.selectTabByObject(tab.obj)
  854.             
  855.         
  856.         util.print_mem_usage('Post-selection memory check')
  857.         item.reconnectDownloaders()
  858.         util.print_mem_usage('Post-item reconnect memory check')
  859.         eventloop.addTimeout(3, autoupdate.checkForUpdates, 'Check for updates')
  860.         feed.expireItems()
  861.         self.tabDisplay = TemplateDisplay('tablist', 'default', playlistTabOrder = playlistTabOrder, channelTabOrder = channelTabOrder)
  862.         self.frame.selectDisplay(self.tabDisplay, self.frame.channelsDisplay)
  863.         self.updateAvailableItemsCountFeedback()
  864.         if self.gatheredVideos is not None and len(self.gatheredVideos) > 0:
  865.             singleclick.resetCommandLineView()
  866.             for v in self.gatheredVideos:
  867.                 
  868.                 try:
  869.                     singleclick.addVideo(v)
  870.                 continue
  871.                 except Exception:
  872.                     e = None
  873.                     logging.info('error while adding file %s', v)
  874.                     logging.info(e)
  875.                     continue
  876.                 
  877.  
  878.             
  879.         
  880.         util.print_mem_usage('Pre single-click memory check')
  881.         eventloop.addIdle(singleclick.parseCommandLineArgs, 'parse command line')
  882.         util.print_mem_usage('Post single-click memory check')
  883.         starttime = clock()
  884.         iconcache.clearOrphans()
  885.         logging.timing('Icon clear: %.3f', clock() - starttime)
  886.         logging.info('Starting movie data updates')
  887.         moviedata.movieDataUpdater.startThread()
  888.         logging.info('Finished startup sequence')
  889.         self.finishStartupSequence()
  890.  
  891.     finalizeStartup = startupFunction(finalizeStartup)
  892.     
  893.     def finishStartupSequence(self):
  894.         self.finishedStartup = True
  895.         frontend.Application.finishStartupSequence(self)
  896.  
  897.     
  898.     def setupGlobalFeed(self, url, *args, **kwargs):
  899.         feedView = views.feeds.filterWithIndex(indexes.feedsByURL, url)
  900.         
  901.         try:
  902.             if feedView.len() == 0:
  903.                 logging.info('Spawning global feed %s', url)
  904.                 d = feed.Feed(url, *args, **kwargs)
  905.             elif feedView.len() > 1:
  906.                 allFeeds = [ f for f in feedView ]
  907.                 for extra in allFeeds[1:]:
  908.                     extra.remove()
  909.                 
  910.                 util.failed('Too many db objects for %s' % url)
  911.         finally:
  912.             feedView.unlink()
  913.  
  914.  
  915.     
  916.     def moviesDirectoryGone(self):
  917.         movies_dir = config.get(prefs.MOVIES_DIRECTORY)
  918.         if not movies_dir.endswith(os.path.sep):
  919.             movies_dir += os.path.sep
  920.         
  921.         
  922.         try:
  923.             contents = os.listdir(movies_dir)
  924.         except OSError:
  925.             return True
  926.  
  927.         if contents != []:
  928.             return False
  929.         
  930.         for downloader in views.remoteDownloads:
  931.             if downloader.isFinished() and downloader.getFilename().startswith(movies_dir):
  932.                 return True
  933.                 continue
  934.         
  935.         return False
  936.  
  937.     
  938.     def getGlobalFeed(self, url):
  939.         feedView = views.feeds.filterWithIndex(indexes.feedsByURL, url)
  940.         rv = feedView[0]
  941.         feedView.unlink()
  942.         return rv
  943.  
  944.     
  945.     def removeGlobalFeed(self, url):
  946.         feedView = views.feeds.filterWithIndex(indexes.feedsByURL, url)
  947.         feedView.resetCursor()
  948.         nextfeed = feedView.getNext()
  949.         feedView.unlink()
  950.         if nextfeed is not None:
  951.             logging.info('Removing global feed %s', url)
  952.             nextfeed.remove()
  953.         
  954.  
  955.     
  956.     def copyCurrentFeedURL(self):
  957.         tabs = self.selection.getSelectedTabs()
  958.         if len(tabs) == 1 and tabs[0].isFeed():
  959.             delegate.copyTextToClipboard(tabs[0].obj.getURL())
  960.         
  961.  
  962.     
  963.     def recommendCurrentFeed(self):
  964.         tabs = self.selection.getSelectedTabs()
  965.         if len(tabs) == 1 and tabs[0].isFeed():
  966.             feed = tabs[0].obj
  967.             query = urllib.urlencode({
  968.                 'url': feed.getURL(),
  969.                 'title': feed.getTitle() })
  970.             delegate.openExternalURL('http://www.videobomb.com/democracy_channel/email_friend?%s' % (query,))
  971.         
  972.  
  973.     
  974.     def copyCurrentItemURL(self):
  975.         tabs = self.selection.getSelectedItems()
  976.         if len(tabs) == 1 and isinstance(tabs[0], item.Item):
  977.             url = tabs[0].getURL()
  978.             if url:
  979.                 delegate.copyTextToClipboard(url)
  980.             
  981.         
  982.  
  983.     
  984.     def selectAllItems(self):
  985.         self.selection.itemListSelection.selectAll()
  986.         self.selection.setTabListActive(False)
  987.  
  988.     
  989.     def removeCurrentSelection(self):
  990.         if self.selection.tabListActive:
  991.             selection = self.selection.tabListSelection
  992.         else:
  993.             selection = self.selection.itemListSelection
  994.         seltype = selection.getType()
  995.         if seltype == 'channeltab':
  996.             self.removeCurrentFeed()
  997.         elif seltype == 'addedguidetab':
  998.             self.removeCurrentGuide()
  999.         elif seltype == 'playlisttab':
  1000.             self.removeCurrentPlaylist()
  1001.         elif seltype == 'item':
  1002.             self.removeCurrentItems()
  1003.         
  1004.  
  1005.     
  1006.     def removeCurrentFeed(self):
  1007.         pass
  1008.  
  1009.     
  1010.     def removeCurrentGuide(self):
  1011.         if self.selection.tabListSelection.getType() == 'addedguidetab':
  1012.             guides = [ t.obj for t in self.selection.getSelectedTabs() ]
  1013.             self.removeGuide(guides[0])
  1014.         
  1015.  
  1016.     
  1017.     def removeCurrentPlaylist(self):
  1018.         pass
  1019.  
  1020.     
  1021.     def removeCurrentItems(self):
  1022.         if self.selection.itemListSelection.getType() != 'item':
  1023.             return None
  1024.         
  1025.         selected = self.selection.getSelectedItems()
  1026.  
  1027.     
  1028.     def renameCurrentTab(self, typeCheckList = None):
  1029.         selected = self.selection.getSelectedTabs()
  1030.         if len(selected) != 1:
  1031.             return None
  1032.         
  1033.         obj = selected[0].obj
  1034.         if typeCheckList is None:
  1035.             typeCheckList = (playlist.SavedPlaylist, folder.ChannelFolder, folder.PlaylistFolder, feed.Feed)
  1036.         
  1037.         if obj.__class__ in typeCheckList:
  1038.             obj.rename()
  1039.         else:
  1040.             logging.warning('Bad object type in renameCurrentTab() %s', obj.__class__)
  1041.  
  1042.     
  1043.     def renameCurrentChannel(self):
  1044.         self.renameCurrentTab(typeCheckList = [
  1045.             feed.Feed,
  1046.             folder.ChannelFolder])
  1047.  
  1048.     
  1049.     def renameCurrentPlaylist(self):
  1050.         self.renameCurrentTab(typeCheckList = [
  1051.             playlist.SavedPlaylist,
  1052.             folder.PlaylistFolder])
  1053.  
  1054.     
  1055.     def downloadCurrentItems(self):
  1056.         selected = self.selection.getSelectedItems()
  1057.         downloadable = _[1]
  1058.         for item in downloadable:
  1059.             item.download()
  1060.         
  1061.  
  1062.     
  1063.     def stopDownloadingCurrentItems(self):
  1064.         selected = self.selection.getSelectedItems()
  1065.         downloading = _[1]
  1066.         for item in downloading:
  1067.             item.expire()
  1068.         
  1069.  
  1070.     
  1071.     def pauseDownloadingCurrentItems(self):
  1072.         selected = self.selection.getSelectedItems()
  1073.         downloading = _[1]
  1074.         for item in downloading:
  1075.             item.pause()
  1076.         
  1077.  
  1078.     
  1079.     def updateCurrentFeed(self):
  1080.         for tab in self.selection.getSelectedTabs():
  1081.             if tab.isFeed():
  1082.                 tab.obj.update()
  1083.                 continue
  1084.         
  1085.  
  1086.     
  1087.     def updateAllFeeds(self):
  1088.         for f in views.feeds:
  1089.             f.update()
  1090.         
  1091.  
  1092.     
  1093.     def removeGuide(self, guide):
  1094.         if guide.getDefault():
  1095.             logging.warning('attempt to remove default guide')
  1096.             return None
  1097.         
  1098.         title = _('Remove %s') % guide.getTitle()
  1099.         description = _('Are you sure you want to remove the guide %s?') % (guide.getTitle(),)
  1100.         dialog = dialogs.ChoiceDialog(title, description, dialogs.BUTTON_YES, dialogs.BUTTON_NO)
  1101.         
  1102.         def dialogCallback(dialog):
  1103.             if guide.idExists() and dialog.choice == dialogs.BUTTON_YES:
  1104.                 guide.remove()
  1105.             
  1106.  
  1107.         dialog.run(dialogCallback)
  1108.  
  1109.     
  1110.     def removePlaylist(self, playlist):
  1111.         return self.removePlaylists([
  1112.             playlist])
  1113.  
  1114.     
  1115.     def removePlaylists(self, playlists):
  1116.         if len(playlists) == 1:
  1117.             title = _('Remove %s') % playlists[0].getTitle()
  1118.             description = _('Are you sure you want to remove %s') % playlists[0].getTitle()
  1119.         else:
  1120.             title = _('Remove %s channels') % len(playlists)
  1121.             description = _('Are you sure you want to remove these %s playlists') % len(playlists)
  1122.         dialog = dialogs.ChoiceDialog(title, description, dialogs.BUTTON_YES, dialogs.BUTTON_NO)
  1123.         
  1124.         def dialogCallback(dialog):
  1125.             if dialog.choice == dialogs.BUTTON_YES:
  1126.                 for playlist in playlists:
  1127.                     if playlist.idExists():
  1128.                         playlist.remove()
  1129.                         continue
  1130.                 
  1131.             
  1132.  
  1133.         dialog.run(dialogCallback)
  1134.  
  1135.     
  1136.     def removeFeed(self, feed):
  1137.         return self.removeFeeds([
  1138.             feed])
  1139.  
  1140.     
  1141.     def removeFeeds(self, feeds):
  1142.         downloads = False
  1143.         downloading = False
  1144.         allDirectories = True
  1145.         for feed in feeds:
  1146.             if isinstance(feed, folder.ChannelFolder) or not feed.getURL().startswith('dtv:directoryfeed'):
  1147.                 allDirectories = False
  1148.                 if feed.hasDownloadedItems():
  1149.                     downloads = True
  1150.                     break
  1151.                 
  1152.                 if feed.hasDownloadingItems():
  1153.                     downloading = True
  1154.                 
  1155.             feed.hasDownloadingItems()
  1156.         
  1157.         if downloads:
  1158.             self.removeFeedsWithDownloads(feeds)
  1159.         elif downloading:
  1160.             self.removeFeedsWithDownloading(feeds)
  1161.         elif allDirectories:
  1162.             self.removeDirectoryFeeds(feeds)
  1163.         else:
  1164.             self.removeFeedsNormal(feeds)
  1165.  
  1166.     
  1167.     def removeFeedsWithDownloads(self, feeds):
  1168.         if len(feeds) == 1:
  1169.             title = _('Remove %s') % feeds[0].getTitle()
  1170.             description = _("What would you like to do with the videos in this channel that you've downloaded?")
  1171.         else:
  1172.             title = _('Remove %s channels') % len(feeds)
  1173.             description = _("What would you like to do with the videos in these channels that you've downloaded?")
  1174.         dialog = dialogs.ThreeChoiceDialog(title, description, dialogs.BUTTON_KEEP_VIDEOS, dialogs.BUTTON_DELETE_VIDEOS, dialogs.BUTTON_CANCEL)
  1175.         
  1176.         def dialogCallback(dialog):
  1177.             if dialog.choice == dialogs.BUTTON_KEEP_VIDEOS:
  1178.                 manualFeed = util.getSingletonDDBObject(views.manualFeed)
  1179.                 for feed in feeds:
  1180.                     if feed.idExists():
  1181.                         feed.remove(moveItemsTo = manualFeed)
  1182.                         continue
  1183.                 
  1184.             elif dialog.choice == dialogs.BUTTON_DELETE_VIDEOS:
  1185.                 for feed in feeds:
  1186.                     if feed.idExists():
  1187.                         feed.remove()
  1188.                         continue
  1189.                 
  1190.             
  1191.  
  1192.         dialog.run(dialogCallback)
  1193.  
  1194.     
  1195.     def removeFeedsWithDownloading(self, feeds):
  1196.         if len(feeds) == 1:
  1197.             title = _('Remove %s') % feeds[0].getTitle()
  1198.             description = _('Are you sure you want to remove %s?  Any downloads in progress will be canceled.') % feeds[0].getTitle()
  1199.         else:
  1200.             title = _('Remove %s channels') % len(feeds)
  1201.             description = _('Are you sure you want to remove these %s channels?  Any downloads in progress will be canceled.') % len(feeds)
  1202.         dialog = dialogs.ChoiceDialog(title, description, dialogs.BUTTON_YES, dialogs.BUTTON_NO)
  1203.         
  1204.         def dialogCallback(dialog):
  1205.             if dialog.choice == dialogs.BUTTON_YES:
  1206.                 for feed in feeds:
  1207.                     if feed.idExists():
  1208.                         feed.remove()
  1209.                         continue
  1210.                 
  1211.             
  1212.  
  1213.         dialog.run(dialogCallback)
  1214.  
  1215.     
  1216.     def removeFeedsNormal(self, feeds):
  1217.         if len(feeds) == 1:
  1218.             title = _('Remove %s') % feeds[0].getTitle()
  1219.             description = _('Are you sure you want to remove %s?') % feeds[0].getTitle()
  1220.         else:
  1221.             title = _('Remove %s channels') % len(feeds)
  1222.             description = _('Are you sure you want to remove these %s channels?') % len(feeds)
  1223.         dialog = dialogs.ChoiceDialog(title, description, dialogs.BUTTON_YES, dialogs.BUTTON_NO)
  1224.         
  1225.         def dialogCallback(dialog):
  1226.             if dialog.choice == dialogs.BUTTON_YES:
  1227.                 for feed in feeds:
  1228.                     if feed.idExists():
  1229.                         feed.remove()
  1230.                         continue
  1231.                 
  1232.             
  1233.  
  1234.         dialog.run(dialogCallback)
  1235.  
  1236.     
  1237.     def removeDirectoryFeeds(self, feeds):
  1238.         if len(feeds) == 1:
  1239.             title = _('Stop watching %s') % feeds[0].getTitle()
  1240.             description = _('Are you sure you want to stop watching %s?') % feeds[0].getTitle()
  1241.         else:
  1242.             title = _('Stop watching %s directories') % len(feeds)
  1243.             description = _('Are you sure you want to stop watching these %s directories?') % len(feeds)
  1244.         dialog = dialogs.ChoiceDialog(title, description, dialogs.BUTTON_YES, dialogs.BUTTON_NO)
  1245.         
  1246.         def dialogCallback(dialog):
  1247.             if dialog.choice == dialogs.BUTTON_YES:
  1248.                 for feed in feeds:
  1249.                     if feed.idExists():
  1250.                         feed.remove()
  1251.                         continue
  1252.                 
  1253.             
  1254.  
  1255.         dialog.run(dialogCallback)
  1256.  
  1257.     
  1258.     def playView(self, view, firstItemId = None, justPlayOne = False):
  1259.         self.playbackController.configure(view, firstItemId, justPlayOne)
  1260.         self.playbackController.enterPlayback()
  1261.  
  1262.     
  1263.     def downloaderShutdown(self):
  1264.         logging.info('Closing Database...')
  1265.         database.defaultDatabase.liveStorage.close()
  1266.         logging.info('Shutting down event loop')
  1267.         eventloop.quit()
  1268.         logging.info('Shutting down frontend')
  1269.         frontend.quit()
  1270.  
  1271.     
  1272.     def quit(self):
  1273.         if self.inQuit:
  1274.             return None
  1275.         
  1276.         downloadsCount = views.downloadingItems.len()
  1277.         if downloadsCount > 0 or config.get(prefs.WARN_IF_DOWNLOADING_ON_QUIT) or self.sendingCrashReport > 0:
  1278.             title = _('Are you sure you want to quit?')
  1279.             if self.sendingCrashReport > 0:
  1280.                 message = _('Miro is still uploading your crash report. If you quit now the upload will be canceled.  Quit Anyway?')
  1281.                 dialog = dialogs.ChoiceDialog(title, message, dialogs.BUTTON_QUIT, dialogs.BUTTON_CANCEL)
  1282.             else:
  1283.                 message = ngettext('You have %d download still in progress.  Quit Anyway?', 'You have %d downloads still in progress.  Quit Anyway?', downloadsCount) % (downloadsCount,)
  1284.                 warning = _('Warn me when I attempt to quit with downloads in progress')
  1285.                 dialog = dialogs.CheckboxDialog(title, message, warning, True, dialogs.BUTTON_QUIT, dialogs.BUTTON_CANCEL)
  1286.             
  1287.             def callback(dialog):
  1288.                 if dialog.choice == dialogs.BUTTON_QUIT:
  1289.                     if isinstance(dialog, dialogs.CheckboxDialog):
  1290.                         config.set(prefs.WARN_IF_DOWNLOADING_ON_QUIT, dialog.checkbox_value)
  1291.                     
  1292.                     self.quitStage2()
  1293.                 else:
  1294.                     self.inQuit = False
  1295.  
  1296.             dialog.run(callback)
  1297.             self.inQuit = True
  1298.         else:
  1299.             self.quitStage2()
  1300.  
  1301.     quit = eventloop.asUrgent(quit)
  1302.     
  1303.     def quitStage2(self):
  1304.         logging.info('Shutting down Downloader...')
  1305.         downloader.shutdownDownloader(self.downloaderShutdown)
  1306.  
  1307.     
  1308.     def setGuideURL(self, guideURL):
  1309.         '''Change the URL of the current channel guide being displayed.  If no
  1310.         guide is being display, pass in None.
  1311.  
  1312.         This method must be called from the onSelectedTabChange in the
  1313.         platform code.  URLs are legal within guideURL will be allow
  1314.         through in onURLLoad().
  1315.         '''
  1316.         self.guide = None
  1317.         if guideURL is not None:
  1318.             self.guideURL = guideURL
  1319.             for guideObj in views.guides:
  1320.                 if guideObj.getURL() == controller.guideURL:
  1321.                     self.guide = guideObj
  1322.                     continue
  1323.             
  1324.         else:
  1325.             self.guideURL = None
  1326.  
  1327.     setGuideURL = eventloop.asUrgent(setGuideURL)
  1328.     
  1329.     def setLastVisitedGuideURL(self, url):
  1330.         selectedTabs = self.selection.getSelectedTabs()
  1331.         selectedObjects = [ t.obj for t in selectedTabs ]
  1332.         if selectedObjects[0].isPartOfGuide(url):
  1333.             if url.startswith(u'http://') or url.startswith(u'https://'):
  1334.                 selectedObjects[0].lastVisitedURL = url
  1335.                 selectedObjects[0].extendHistory(url)
  1336.             else:
  1337.                 logging.warn('setLastVisitedGuideURL called, but the guide is no longer selected')
  1338.  
  1339.     setLastVisitedGuideURL = eventloop.asIdle(setLastVisitedGuideURL)
  1340.     
  1341.     def onShutdown(self):
  1342.         
  1343.         try:
  1344.             eventloop.join()
  1345.             logging.info('Saving preferences...')
  1346.             config.save()
  1347.             logging.info('Shutting down icon cache updates')
  1348.             iconcache.iconCacheUpdater.shutdown()
  1349.             logging.info('Shutting down movie data updates')
  1350.             moviedata.movieDataUpdater.shutdown()
  1351.             if self.idlingNotifier is not None:
  1352.                 logging.info('Shutting down IdleNotifier')
  1353.                 self.idlingNotifier.join()
  1354.             
  1355.             logging.info('Done shutting down.')
  1356.             logging.info('Remaining threads are:')
  1357.             for thread in threading.enumerate():
  1358.                 logging.info('%s', thread)
  1359.         except:
  1360.             util.failedExn('while shutting down')
  1361.             frontend.exit(1)
  1362.  
  1363.  
  1364.     
  1365.     def configDidChange(self, key, value):
  1366.         if key is prefs.LIMIT_UPSTREAM.key:
  1367.             if value is False:
  1368.                 
  1369.                 try:
  1370.                     self.idlingNotifier.join()
  1371.                 except:
  1372.                     pass
  1373.  
  1374.                 self.idlingNotifier = None
  1375.             elif self.idlingNotifier is None:
  1376.                 self.idlingNotifier = idlenotifier.IdleNotifier(self)
  1377.                 self.idlingNotifier.start()
  1378.             
  1379.         
  1380.  
  1381.     
  1382.     def systemHasBeenIdlingSince(self, seconds):
  1383.         self.setUpstreamLimit(False)
  1384.  
  1385.     
  1386.     def systemIsActiveAgain(self):
  1387.         self.setUpstreamLimit(True)
  1388.  
  1389.     
  1390.     def addAndSelectFeed(self, url = None, showTemplate = None):
  1391.         return GUIActionHandler().addFeed(url, showTemplate)
  1392.  
  1393.     
  1394.     def addAndSelectGuide(self, url = None):
  1395.         return GUIActionHandler().addGuide(url)
  1396.  
  1397.     
  1398.     def addSearchFeed(self, term = None, style = dialogs.SearchChannelDialog.CHANNEL, location = None):
  1399.         return GUIActionHandler().addSearchFeed(term, style, location)
  1400.  
  1401.     
  1402.     def testSearchFeedDialog(self):
  1403.         return GUIActionHandler().testSearchFeedDialog()
  1404.  
  1405.     
  1406.     def addFeed(self, url = None):
  1407.         return GUIActionHandler().addFeed(url, selected = None)
  1408.  
  1409.     
  1410.     def selectFeed(self, url):
  1411.         return GUIActionHandler().selectFeed(url)
  1412.  
  1413.     
  1414.     def onUnwatchedItemsCountChange(self, obj, id):
  1415.         if not self.newTab is not None:
  1416.             raise AssertionError
  1417.         self.newTab.redraw()
  1418.         self.updateAvailableItemsCountFeedback()
  1419.         if hasattr(frontend.Application, 'onUnwatchedItemsCountChange'):
  1420.             frontend.Application.onUnwatchedItemsCountChange(self, obj, id)
  1421.         
  1422.  
  1423.     
  1424.     def onDownloadingItemsCountChange(self, obj, id):
  1425.         if not self.downloadTab is not None:
  1426.             raise AssertionError
  1427.         self.downloadTab.redraw()
  1428.         if hasattr(frontend.Application, 'onDownloadingItemsCountChange'):
  1429.             frontend.Application.onDownloadingItemsCountChange(self, obj, id)
  1430.         
  1431.  
  1432.     
  1433.     def updateAvailableItemsCountFeedback(self):
  1434.         count = views.unwatchedItems.len()
  1435.         delegate.updateAvailableItemsCountFeedback(count)
  1436.  
  1437.     
  1438.     def performSearch(self, engine, query):
  1439.         util.checkU(engine)
  1440.         util.checkU(query)
  1441.         handler = TemplateActionHandler(None, None)
  1442.         handler.updateLastSearchEngine(engine)
  1443.         handler.updateLastSearchQuery(query)
  1444.         handler.performSearch(engine, query)
  1445.         self.selection.selectTabByTemplateBase('searchtab')
  1446.  
  1447.     
  1448.     def setUpstreamLimit(self, setLimit):
  1449.         if setLimit:
  1450.             limit = config.get(prefs.UPSTREAM_LIMIT_IN_KBS)
  1451.         
  1452.  
  1453.     
  1454.     def handleURIDrop(self, data, **kwargs):
  1455.         '''Handle an external drag that contains a text/uri-list mime-type.
  1456.         data should be the text/uri-list data, in escaped form.
  1457.  
  1458.         kwargs is thrown away.  It exists to catch weird URLs, like
  1459.         javascript: which sometime result in us getting extra arguments.
  1460.         '''
  1461.         lastAddedFeed = None
  1462.         data = urllib.unquote(data)
  1463.         for url in data.split(u'\n'):
  1464.             url = url.strip()
  1465.             if url == u'':
  1466.                 continue
  1467.             
  1468.             if url.startswith(u'file://'):
  1469.                 filename = download_utils.getFileURLPath(url)
  1470.                 filename = platformutils.osFilenameToFilenameType(filename)
  1471.                 eventloop.addIdle(singleclick.openFile, 'Open Dropped file', args = (filename,))
  1472.                 continue
  1473.             if url.startswith(u'http:') or url.startswith(u'https:'):
  1474.                 url = feed.normalizeFeedURL(url)
  1475.                 if feed.validateFeedURL(url) and not feed.getFeedByURL(url):
  1476.                     lastAddedFeed = feed.Feed(url)
  1477.                 
  1478.             not feed.getFeedByURL(url)
  1479.         
  1480.         if lastAddedFeed:
  1481.             controller.selection.selectTabByObject(lastAddedFeed)
  1482.         
  1483.  
  1484.     
  1485.     def handleDrop(self, dropData, type, sourceData):
  1486.         
  1487.         try:
  1488.             (destType, destID) = dropData.split('-')
  1489.             if destID == 'END':
  1490.                 destObj = None
  1491.             elif destID == 'START':
  1492.                 if destType == 'channel':
  1493.                     tabOrder = util.getSingletonDDBObject(views.channelTabOrder)
  1494.                 else:
  1495.                     tabOrder = util.getSingletonDDBObject(views.playlistTabOrder)
  1496.                 for tab in tabOrder.getView():
  1497.                     destObj = tab.obj
  1498.                 
  1499.             else:
  1500.                 destObj = db.getObjectByID(int(destID))
  1501.             (sourceArea, sourceID) = sourceData.split('-')
  1502.             sourceID = int(sourceID)
  1503.             draggedIDs = self.selection.calcSelection(sourceArea, sourceID)
  1504.         except:
  1505.             logging.exception('error parsing drop (%r, %r, %r)', dropData, type, sourceData)
  1506.             return None
  1507.  
  1508.         if destType == 'playlist' and type == 'downloadeditem':
  1509.             destObj.handleDNDAppend(draggedIDs)
  1510.         elif (destType == 'channelfolder' or type == 'channel' or destType == 'playlistfolder') and type == 'playlist':
  1511.             obj = db.getObjectByID(int(destID))
  1512.             obj.handleDNDAppend(draggedIDs)
  1513.         elif destType in ('playlist', 'playlistfolder') and type in ('playlist', 'playlistfolder'):
  1514.             tabOrder = util.getSingletonDDBObject(views.playlistTabOrder)
  1515.             tabOrder.handleDNDReorder(destObj, draggedIDs)
  1516.         elif destType in ('channel', 'channelfolder') and type in ('channel', 'channelfolder'):
  1517.             tabOrder = util.getSingletonDDBObject(views.channelTabOrder)
  1518.             tabOrder.handleDNDReorder(destObj, draggedIDs)
  1519.         elif destType == 'playlistitem' and type == 'downloadeditem':
  1520.             playlist = self.selection.getSelectedTabs()[0].obj
  1521.             playlist.handleDNDReorder(destObj, draggedIDs)
  1522.         else:
  1523.             logging.info("Can't handle drop. Dest type: %s Dest id: %s Type: %s", destType, destID, type)
  1524.  
  1525.     
  1526.     def addToNewPlaylist(self):
  1527.         selected = controller.selection.getSelectedItems()
  1528.         childIDs = _[1]
  1529.         playlist.createNewPlaylist(childIDs)
  1530.  
  1531.     
  1532.     def startUploads(self):
  1533.         selected = controller.selection.getSelectedItems()
  1534.         for i in selected:
  1535.             i.startUpload()
  1536.         
  1537.  
  1538.     
  1539.     def newDownload(self, url = None):
  1540.         return GUIActionHandler().addDownload(url)
  1541.  
  1542.     
  1543.     def importChannels(self):
  1544.         importer = opml.Importer()
  1545.         importer.importSubscriptions()
  1546.  
  1547.     
  1548.     def exportChannels(self):
  1549.         exporter = opml.Exporter()
  1550.         exporter.exportSubscriptions()
  1551.  
  1552.  
  1553.  
  1554. class TemplateDisplay(frontend.HTMLDisplay):
  1555.     
  1556.     def __init__(self, templateName, templateState, frameHint = None, areaHint = None, baseURL = None, *args, **kargs):
  1557.         """'templateName' is the name of the inital template file.  'data' is
  1558.         keys for the template. 'templateState' is a string with the state of the
  1559.         template.
  1560.         """
  1561.         logging.debug('Processing %s', templateName)
  1562.         self.templateName = templateName
  1563.         self.templateState = templateState
  1564.         (tch, self.templateHandle) = template.fillTemplate(templateName, self, self.getDTVPlatformName(), self.getEventCookie(), self.getBodyTagExtra(), templateState = templateState, *args, **kargs)
  1565.         self.args = args
  1566.         self.kargs = kargs
  1567.         self.haveLoaded = False
  1568.         html = tch.read()
  1569.         self.actionHandlers = [
  1570.             ModelActionHandler(delegate),
  1571.             HistoryActionHandler(self),
  1572.             GUIActionHandler(),
  1573.             TemplateActionHandler(self, self.templateHandle)]
  1574.         loadTriggers = self.templateHandle.getTriggerActionURLsOnLoad()
  1575.         newPage = self.runActionURLs(loadTriggers)
  1576.         if newPage:
  1577.             self.templateHandle.unlinkTemplate()
  1578.             self.__init__(re.compile('^template:(.*)$').match(url).group(1), frameHint, areaHint, baseURL)
  1579.         else:
  1580.             frontend.HTMLDisplay.__init__(self, html, frameHint = frameHint, areaHint = areaHint, baseURL = baseURL)
  1581.             self.templateHandle.initialFillIn()
  1582.  
  1583.     
  1584.     def __eq__(self, other):
  1585.         if other.__class__ == TemplateDisplay and self.templateName == other.templateName and self.args == other.args:
  1586.             pass
  1587.         return self.kargs == other.kargs
  1588.  
  1589.     
  1590.     def __str__(self):
  1591.         return 'Template <%s> args=%s kargs=%s' % (self.templateName, self.args, self.kargs)
  1592.  
  1593.     
  1594.     def reInit(self, *args, **kargs):
  1595.         self.args = args
  1596.         self.kargs = kargs
  1597.         
  1598.         try:
  1599.             self.templateHandle.templateVars['reInit'](*args, **kargs)
  1600.         except:
  1601.             pass
  1602.  
  1603.         self.templateHandle.forceUpdate()
  1604.  
  1605.     
  1606.     def runActionURLs(self, triggers):
  1607.         newPage = False
  1608.         for url in triggers:
  1609.             if url.startswith('action:'):
  1610.                 self.onURLLoad(url)
  1611.                 continue
  1612.             if url.startswith('template:'):
  1613.                 newPage = True
  1614.                 break
  1615.                 continue
  1616.         
  1617.         return newPage
  1618.  
  1619.     
  1620.     def parseEventURL(self, url):
  1621.         match = re.match('[a-zA-Z]+:([^?]+)(\\?(.*))?$', url)
  1622.         if match:
  1623.             path = match.group(1)
  1624.             argString = match.group(3)
  1625.             if argString is None:
  1626.                 argString = u''
  1627.             
  1628.             argString = argString.encode('utf8')
  1629.             argLists = cgi.parse_qs(argString, keep_blank_values = True)
  1630.             args = { }
  1631.             for key in argLists.keys():
  1632.                 value = argLists[key]
  1633.                 if len(value) != 1:
  1634.                     import template_compiler
  1635.                     raise template_compiler.TemplateError, "Multiple values of '%s' argument passed to '%s' action" % (key, url)
  1636.                 
  1637.                 
  1638.                 try:
  1639.                     args[key.encode('ascii', 'replace')] = value[0].decode('utf8')
  1640.                 continue
  1641.                 args[key.encode('ascii', 'replace')] = value[0].decode('ascii', 'replace')
  1642.                 continue
  1643.  
  1644.             
  1645.             return (path, args)
  1646.         else:
  1647.             raise ValueError('Badly formed eventURL: %s' % url)
  1648.  
  1649.     
  1650.     def onURLLoad(self, url):
  1651.         if self.checkURL(url):
  1652.             if not controller.guide:
  1653.                 return True
  1654.             
  1655.             if not (self.haveLoaded) and url == controller.guide.getLastVisitedURL():
  1656.                 self.haveLoaded = True
  1657.             elif self.haveLoaded:
  1658.                 script = 'top.miro_navigation_frame.guideUnloaded()'
  1659.                 if not url.endswith(script):
  1660.                     self.execJS('top.miro_navigation_frame.guideUnloaded()')
  1661.                 
  1662.             
  1663.             return True
  1664.         else:
  1665.             return False
  1666.  
  1667.     
  1668.     def checkURL(self, url):
  1669.         util.checkU(url)
  1670.         logging.info('got %s', url)
  1671.         
  1672.         try:
  1673.             if url.startswith(u'template:'):
  1674.                 (name, args) = self.parseEventURL(url)
  1675.                 self.dispatchAction('switchTemplate', name = name, **args)
  1676.                 return False
  1677.             
  1678.             if url.startswith(u'action:'):
  1679.                 (action, args) = self.parseEventURL(url)
  1680.                 self.dispatchAction(action, **args)
  1681.                 return False
  1682.             
  1683.             if controller.guide is not None and controller.guide.isPartOfGuide(url):
  1684.                 controller.setLastVisitedGuideURL(url)
  1685.                 return True
  1686.             
  1687.             if url.startswith(u'file://'):
  1688.                 path = download_utils.getFileURLPath(url)
  1689.                 return os.path.exists(path)
  1690.             
  1691.             if url.startswith(u'http://') and url.startswith(u'https://') and url.startswith(u'ftp://') and url.startswith(u'mailto:') or url.startswith(u'feed://'):
  1692.                 self.handleCandidateExternalURL(url)
  1693.                 return False
  1694.         except:
  1695.             details = "Handling action URL '%s'" % (url,)
  1696.             util.failedExn('while handling a request', details = details)
  1697.  
  1698.         return True
  1699.  
  1700.     
  1701.     def handleCandidateExternalURL(self, url):
  1702.         """Open a URL that onURLLoad thinks is an external URL.
  1703.         handleCandidateExternalURL does extra checks that onURLLoad can't do
  1704.         because it's happens in the gui thread and can't access the DB.
  1705.         """
  1706.         (type, subscribeURLs) = subscription.findSubscribeLinks(url)
  1707.         if len(subscribeURLs) == 0:
  1708.             for guideObj in views.guides:
  1709.                 if guideObj.isPartOfGuide(url):
  1710.                     return None
  1711.                     continue
  1712.             
  1713.         
  1714.         normalizedURLs = []
  1715.         for url in subscribeURLs:
  1716.             normalized = feed.normalizeFeedURL(url)
  1717.             if feed.validateFeedURL(normalized):
  1718.                 normalizedURLs.append(normalized)
  1719.                 continue
  1720.         
  1721.         if normalizedURLs:
  1722.             if type == 'feed':
  1723.                 for url in normalizedURLs:
  1724.                     if feed.getFeedByURL(url) is None:
  1725.                         newFeed = feed.Feed(url)
  1726.                         newFeed.blink()
  1727.                         continue
  1728.                 
  1729.             elif type == 'download':
  1730.                 for url in normalizedURLs:
  1731.                     filename = platformutils.unicodeToFilename(url)
  1732.                     singleclick.downloadURL(filename)
  1733.                 
  1734.             elif type == 'guide':
  1735.                 for url in normalizedURLs:
  1736.                     if guide.getGuideByURL(url) is None:
  1737.                         guide.ChannelGuide(url)
  1738.                         continue
  1739.                 
  1740.             else:
  1741.                 raise AssertionError('Unkown subscribe type')
  1742.             return None
  1743.         
  1744.         if url.startswith(u'feed://'):
  1745.             url = u'http://' + url[len(u'feed://'):]
  1746.             f = feed.getFeedByURL(url)
  1747.             if f is None:
  1748.                 f = feed.Feed(url)
  1749.             
  1750.             f.blink()
  1751.             return None
  1752.         
  1753.         delegate.openExternalURL(url)
  1754.  
  1755.     handleCandidateExternalURL = eventloop.asUrgent(handleCandidateExternalURL)
  1756.     
  1757.     def dispatchAction(self, action, **kwargs):
  1758.         called = False
  1759.         start = clock()
  1760.         for handler in self.actionHandlers:
  1761.             if hasattr(handler, action):
  1762.                 getattr(handler, action)(**kwargs)
  1763.                 called = True
  1764.                 break
  1765.                 continue
  1766.         
  1767.         end = clock()
  1768.         if end - start > 0.5:
  1769.             logging.timing('dispatch action %s too slow (%.3f secs)', action, end - start)
  1770.         
  1771.         if not called:
  1772.             logging.warning('Ignored bad action URL: action=%s', action)
  1773.         
  1774.  
  1775.     dispatchAction = eventloop.asUrgent(dispatchAction)
  1776.     
  1777.     def onDeselected(self, frame):
  1778.         unloadTriggers = self.templateHandle.getTriggerActionURLsOnUnload()
  1779.         self.runActionURLs(unloadTriggers)
  1780.         self.unlink()
  1781.         frontend.HTMLDisplay.onDeselected(self, frame)
  1782.  
  1783.     onDeselected = eventloop.asUrgent(onDeselected)
  1784.     
  1785.     def unlink(self):
  1786.         self.templateHandle.unlinkTemplate()
  1787.         self.actionHandlers = []
  1788.  
  1789.  
  1790.  
  1791. class ModelActionHandler:
  1792.     
  1793.     def __init__(self, backEndDelegate):
  1794.         self.backEndDelegate = backEndDelegate
  1795.  
  1796.     
  1797.     def setAutoDownloadMode(self, feed, mode):
  1798.         obj = db.getObjectByID(int(feed))
  1799.         obj.setAutoDownloadMode(mode)
  1800.  
  1801.     
  1802.     def setExpiration(self, feed, type, time):
  1803.         obj = db.getObjectByID(int(feed))
  1804.         obj.setExpiration(type, int(time))
  1805.  
  1806.     
  1807.     def setMaxNew(self, feed, maxNew):
  1808.         obj = db.getObjectByID(int(feed))
  1809.         obj.setMaxNew(int(maxNew))
  1810.  
  1811.     
  1812.     def invalidMaxNew(self, value):
  1813.         title = _('Invalid Value')
  1814.         description = _('%s is invalid.  You must enter a non-negative number.') % value
  1815.         dialogs.MessageBoxDialog(title, description).run()
  1816.  
  1817.     
  1818.     def startDownload(self, item):
  1819.         
  1820.         try:
  1821.             obj = db.getObjectByID(int(item))
  1822.             obj.download()
  1823.         except database.ObjectNotFoundError:
  1824.             pass
  1825.  
  1826.  
  1827.     
  1828.     def removeFeed(self, id):
  1829.         
  1830.         try:
  1831.             feed = db.getObjectByID(int(id))
  1832.             controller.removeFeed(feed)
  1833.         except database.ObjectNotFoundError:
  1834.             pass
  1835.  
  1836.  
  1837.     
  1838.     def removeCurrentFeed(self):
  1839.         controller.removeCurrentFeed()
  1840.  
  1841.     
  1842.     def removeCurrentPlaylist(self):
  1843.         controller.removeCurrentPlaylist()
  1844.  
  1845.     
  1846.     def removeCurrentItems(self):
  1847.         controller.removeCurrentItems()
  1848.  
  1849.     
  1850.     def mergeToFolder(self):
  1851.         tls = controller.selection.tabListSelection
  1852.         selectionType = tls.getType()
  1853.         childIDs = set(tls.currentSelection)
  1854.         if selectionType == 'channeltab':
  1855.             folder.createNewChannelFolder(childIDs)
  1856.         elif selectionType == 'playlisttab':
  1857.             folder.createNewPlaylistFolder(childIDs)
  1858.         else:
  1859.             logging.warning('bad selection type %s in mergeToFolder', selectionType)
  1860.  
  1861.     
  1862.     def remove(self, area, id):
  1863.         selectedIDs = controller.selection.calcSelection(area, int(id))
  1864.         selectedObjects = [ db.getObjectByID(id) for id in selectedIDs ]
  1865.         objType = selectedObjects[0].__class__
  1866.         if objType in (feed.Feed, folder.ChannelFolder):
  1867.             controller.removeFeeds(selectedObjects)
  1868.         elif objType in (playlist.SavedPlaylist, folder.PlaylistFolder):
  1869.             controller.removePlaylists(selectedObjects)
  1870.         elif objType == guide.ChannelGuide:
  1871.             if len(selectedObjects) != 1:
  1872.                 raise AssertionError('Multiple guides selected in remove')
  1873.             
  1874.             controller.removeGuide(selectedObjects[0])
  1875.         elif objType == item.Item:
  1876.             pl = controller.selection.getSelectedTabs()[0].obj
  1877.             pl.handleRemove(destObj, selectedIDs)
  1878.         else:
  1879.             logging.warning("Can't handle type %s in remove()", objType)
  1880.  
  1881.     
  1882.     def rename(self, id):
  1883.         
  1884.         try:
  1885.             obj = db.getObjectByID(int(id))
  1886.         except:
  1887.             logging.warning("tried to rename object that doesn't exist with id %d", int(feed))
  1888.             return None
  1889.  
  1890.         if obj.__class__ in (playlist.SavedPlaylist, folder.ChannelFolder, folder.PlaylistFolder):
  1891.             obj.rename()
  1892.         else:
  1893.             logging.warning('Unknown object type in remove() %s', type(obj))
  1894.  
  1895.     
  1896.     def updateFeed(self, feed):
  1897.         obj = db.getObjectByID(int(feed))
  1898.         obj.update()
  1899.  
  1900.     
  1901.     def copyFeedURL(self, feed):
  1902.         obj = db.getObjectByID(int(feed))
  1903.         url = obj.getURL()
  1904.         self.backEndDelegate.copyTextToClipboard(url)
  1905.  
  1906.     
  1907.     def markFeedViewed(self, feed):
  1908.         
  1909.         try:
  1910.             obj = db.getObjectByID(int(feed))
  1911.             obj.markAsViewed()
  1912.         except database.ObjectNotFoundError:
  1913.             pass
  1914.  
  1915.  
  1916.     
  1917.     def updateIcons(self, feed):
  1918.         
  1919.         try:
  1920.             obj = db.getObjectByID(int(feed))
  1921.             obj.updateIcons()
  1922.         except database.ObjectNotFoundError:
  1923.             pass
  1924.  
  1925.  
  1926.     
  1927.     def expireItem(self, item):
  1928.         
  1929.         try:
  1930.             obj = db.getObjectByID(int(item))
  1931.             obj.expire()
  1932.         except database.ObjectNotFoundError:
  1933.             logging.warning("tried to expire item that doesn't exist with id %d", int(item))
  1934.  
  1935.  
  1936.     
  1937.     def expirePlayingItem(self, item):
  1938.         self.expireItem(item)
  1939.         controller.playbackController.skip(1)
  1940.  
  1941.     
  1942.     def addItemToLibrary(self, item):
  1943.         obj = db.getObjectByID(int(item))
  1944.         manualFeed = util.getSingletonDDBObject(views.manualFeed)
  1945.         obj.setFeed(manualFeed.getID())
  1946.  
  1947.     
  1948.     def keepItem(self, item):
  1949.         obj = db.getObjectByID(int(item))
  1950.         obj.save()
  1951.  
  1952.     
  1953.     def stopUploadItem(self, item):
  1954.         obj = db.getObjectByID(int(item))
  1955.         obj.stopUpload()
  1956.  
  1957.     
  1958.     def toggleMoreItemInfo(self, item):
  1959.         obj = db.getObjectByID(int(item))
  1960.         obj.toggleShowMoreInfo()
  1961.  
  1962.     
  1963.     def revealItem(self, item):
  1964.         obj = db.getObjectByID(int(item))
  1965.         filename = obj.getFilename()
  1966.         if not os.path.exists(filename):
  1967.             basename = os.path.basename(filename)
  1968.             title = _('Error Revealing File')
  1969.             msg = _('The file "%s" was deleted from outside Miro.') % basename
  1970.             dialogs.MessageBoxDialog(title, msg).run()
  1971.         else:
  1972.             self.backEndDelegate.revealFile(filename)
  1973.  
  1974.     
  1975.     def clearTorrents(self):
  1976.         items = views.items.filter((lambda x: if x.getFeed().url == u'dtv:manualFeed' and x.isNonVideoFile() and not (x.getState() == u'paused'):
  1977. passnot (x.getState() == u'downloading')))
  1978.         for i in items:
  1979.             if i.downloader is not None:
  1980.                 i.downloader.setDeleteFiles(False)
  1981.             
  1982.             i.remove()
  1983.         
  1984.  
  1985.     
  1986.     def pauseDownload(self, item):
  1987.         obj = db.getObjectByID(int(item))
  1988.         obj.pause()
  1989.  
  1990.     
  1991.     def resumeDownload(self, item):
  1992.         obj = db.getObjectByID(int(item))
  1993.         obj.resume()
  1994.  
  1995.     
  1996.     def pauseAll(self):
  1997.         autodler.pauseDownloader()
  1998.         for item in views.downloadingItems:
  1999.             item.pause()
  2000.         
  2001.  
  2002.     
  2003.     def resumeAll(self):
  2004.         for item in views.pausedItems:
  2005.             item.resume()
  2006.         
  2007.         autodler.resumeDownloader()
  2008.  
  2009.     
  2010.     def toggleExpand(self, id):
  2011.         obj = db.getObjectByID(int(id))
  2012.         obj.setExpanded(not obj.getExpanded())
  2013.  
  2014.     
  2015.     def setRunAtStartup(self, value):
  2016.         value = value == '1'
  2017.         self.backEndDelegate.setRunAtStartup(value)
  2018.  
  2019.     
  2020.     def setCheckEvery(self, value):
  2021.         value = int(value)
  2022.         config.set(prefs.CHECK_CHANNELS_EVERY_X_MN, value)
  2023.  
  2024.     
  2025.     def setLimitUpstream(self, value):
  2026.         value = value == '1'
  2027.         config.set(prefs.LIMIT_UPSTREAM, value)
  2028.  
  2029.     
  2030.     def setMaxUpstream(self, value):
  2031.         value = int(value)
  2032.         config.set(prefs.UPSTREAM_LIMIT_IN_KBS, value)
  2033.  
  2034.     
  2035.     def setPreserveDiskSpace(self, value):
  2036.         value = value == '1'
  2037.         config.set(prefs.PRESERVE_DISK_SPACE, value)
  2038.  
  2039.     
  2040.     def setDefaultExpiration(self, value):
  2041.         value = int(value)
  2042.         config.set(prefs.EXPIRE_AFTER_X_DAYS, value)
  2043.  
  2044.     
  2045.     def videoBombExternally(self, item):
  2046.         obj = db.getObjectByID(int(item))
  2047.         paramList = { }
  2048.         paramList['title'] = obj.getTitle()
  2049.         paramList['info_url'] = obj.getLink()
  2050.         paramList['hookup_url'] = obj.getPaymentLink()
  2051.         
  2052.         try:
  2053.             rss_url = obj.getFeed().getURL()
  2054.             if not rss_url.startswith(u'dtv:'):
  2055.                 paramList['rss_url'] = rss_url
  2056.         except:
  2057.             pass
  2058.  
  2059.         thumb_url = obj.getThumbnailURL()
  2060.         if thumb_url is not None:
  2061.             paramList['thumb_url'] = thumb_url
  2062.         
  2063.         paramString = ''
  2064.         glue = '?'
  2065.         url = obj.getURL()
  2066.         url.encode('utf-8', 'replace')
  2067.         if not url.startswith('file:'):
  2068.             paramString = '?url=%s' % xhtmltools.urlencode(url)
  2069.             glue = '&'
  2070.         
  2071.         for key in paramList.keys():
  2072.             if len(paramList[key]) > 0:
  2073.                 paramString = '%s%s%s=%s' % (paramString, glue, key, xhtmltools.urlencode(paramList[key]))
  2074.                 glue = '&'
  2075.                 continue
  2076.         
  2077.         description = obj.getDescription()
  2078.         if len(description) > 0:
  2079.             paramString = '%s%sdescription=%s' % (paramString, glue, xhtmltools.urlencode(description))
  2080.         
  2081.         url = config.get(prefs.VIDEOBOMB_URL) + paramString
  2082.         self.backEndDelegate.openExternalURL(url)
  2083.  
  2084.     
  2085.     def changeMoviesDirectory(self, newDir, migrate):
  2086.         changeMoviesDirectory(newDir, migrate == '1')
  2087.  
  2088.  
  2089.  
  2090. class printResultThread(threading.Thread):
  2091.     
  2092.     def __init__(self, format, func):
  2093.         self.format = format
  2094.         self.func = func
  2095.         threading.Thread.__init__(self)
  2096.  
  2097.     
  2098.     def run(self):
  2099.         print self.format % (self.func(),)
  2100.  
  2101.  
  2102.  
  2103. class HistoryActionHandler:
  2104.     
  2105.     def __init__(self, display):
  2106.         self.display = display
  2107.  
  2108.     
  2109.     def gotoURL(self, newURL):
  2110.         self.display.execJS('top.miro_guide_frame.location="%s"' % newURL)
  2111.  
  2112.     
  2113.     def getGuide(self):
  2114.         guides = [ t.obj for t in controller.selection.getSelectedTabs() ]
  2115.         if not isinstance(guides[0], guide.ChannelGuide):
  2116.             return None
  2117.         
  2118.         return guides[0]
  2119.  
  2120.     
  2121.     def back(self):
  2122.         guide = self.getGuide()
  2123.         if guide is not None:
  2124.             newURL = guide.getHistoryURL(-1)
  2125.             if newURL is not None:
  2126.                 self.gotoURL(newURL)
  2127.             
  2128.         
  2129.  
  2130.     
  2131.     def forward(self):
  2132.         guide = self.getGuide()
  2133.         if guide is not None:
  2134.             newURL = guide.getHistoryURL(1)
  2135.             if newURL is not None:
  2136.                 self.gotoURL(newURL)
  2137.             
  2138.         
  2139.  
  2140.     
  2141.     def home(self):
  2142.         guide = self.getGuide()
  2143.         if guide is not None:
  2144.             newURL = guide.getHistoryURL(None)
  2145.             self.gotoURL(newURL)
  2146.         
  2147.  
  2148.  
  2149.  
  2150. class GUIActionHandler:
  2151.     
  2152.     def playUnwatched(self):
  2153.         controller.playView(views.unwatchedItems)
  2154.  
  2155.     
  2156.     def openFile(self, path):
  2157.         singleclick.openFile(path)
  2158.  
  2159.     
  2160.     def addSearchFeed(self, term = None, style = dialogs.SearchChannelDialog.CHANNEL, location = None):
  2161.         
  2162.         def doAdd(dialog):
  2163.             if dialog.choice == dialogs.BUTTON_CREATE_CHANNEL:
  2164.                 self.addFeed(dialog.getURL())
  2165.             
  2166.  
  2167.         dialog = dialogs.SearchChannelDialog(term, style, location)
  2168.         if location == None:
  2169.             dialog.run(doAdd)
  2170.         else:
  2171.             self.addFeed(dialog.getURL())
  2172.  
  2173.     
  2174.     def addChannelSearchFeed(self, id):
  2175.         feed = db.getObjectByID(int(id))
  2176.         self.addSearchFeed(feed.inlineSearchTerm, dialogs.SearchChannelDialog.CHANNEL, int(id))
  2177.  
  2178.     
  2179.     def addEngineSearchFeed(self, term, name):
  2180.         self.addSearchFeed(term, dialogs.SearchChannelDialog.ENGINE, name)
  2181.  
  2182.     
  2183.     def testSearchFeedDialog(self):
  2184.         
  2185.         def finish(dialog):
  2186.             pass
  2187.  
  2188.         
  2189.         def thirdDialog(dialog):
  2190.             dialog = dialogs.SearchChannelDialog('Should select URL http://testurl/', dialogs.SearchChannelDialog.URL, 'http://testurl/')
  2191.             dialog.run(finish)
  2192.  
  2193.         
  2194.         def secondDialog(dialog):
  2195.             dialog = dialogs.SearchChannelDialog('Should select YouTube engine', dialogs.SearchChannelDialog.ENGINE, 'youtube')
  2196.             dialog.run(thirdDialog)
  2197.  
  2198.         dialog = dialogs.SearchChannelDialog('Should select third channel in list', dialogs.SearchChannelDialog.CHANNEL, -1)
  2199.         dialog.run(secondDialog)
  2200.  
  2201.     
  2202.     def addURL(self, title, message, callback, url = None):
  2203.         util.checkU(url)
  2204.         util.checkU(title)
  2205.         util.checkU(message)
  2206.         
  2207.         def createDialog(ltitle, lmessage, prefill = (None,)):
  2208.             
  2209.             def prefillCallback():
  2210.                 if prefill:
  2211.                     return prefill
  2212.                 else:
  2213.                     return None
  2214.  
  2215.             dialog = dialogs.TextEntryDialog(ltitle, lmessage, dialogs.BUTTON_OK, dialogs.BUTTON_CANCEL, prefillCallback, fillWithClipboardURL = prefill is None)
  2216.             
  2217.             def callback(dialog):
  2218.                 if dialog.choice == dialogs.BUTTON_OK:
  2219.                     doAdd(dialog.value)
  2220.                 
  2221.  
  2222.             dialog.run(callback)
  2223.  
  2224.         
  2225.         def doAdd(url):
  2226.             normalizedURL = feed.normalizeFeedURL(url)
  2227.             if not feed.validateFeedURL(normalizedURL):
  2228.                 ltitle = title + _(' - Invalid URL')
  2229.                 lmessage = _('The address you entered is not a valid URL.\nPlease double check and try again.\n\n') + message
  2230.                 createDialog(ltitle, lmessage, url)
  2231.                 return None
  2232.             
  2233.             callback(normalizedURL)
  2234.  
  2235.         if url is None:
  2236.             createDialog(title, message)
  2237.         else:
  2238.             doAdd(url)
  2239.  
  2240.     
  2241.     def addFeed(self, url = None, showTemplate = None, selected = '1'):
  2242.         if url:
  2243.             util.checkU(url)
  2244.         
  2245.         
  2246.         def doAdd(url):
  2247.             db.confirmDBThread()
  2248.             myFeed = feed.getFeedByURL(url)
  2249.             if myFeed is None:
  2250.                 myFeed = feed.Feed(url)
  2251.             
  2252.             if selected == '1':
  2253.                 controller.selection.selectTabByObject(myFeed)
  2254.             else:
  2255.                 myFeed.blink()
  2256.  
  2257.         self.addURL(Template(_('$shortAppName - Add Channel')).substitute(shortAppName = config.get(prefs.SHORT_APP_NAME)), _('Enter the URL of the channel to add'), doAdd, url)
  2258.  
  2259.     
  2260.     def selectFeed(self, url):
  2261.         url = feed.normalizeFeedURL(url)
  2262.         db.confirmDBThread()
  2263.         myFeed = feed.getFeedByURL(url)
  2264.         if myFeed is None:
  2265.             logging.warning('selectFeed: no such feed: %s', url)
  2266.             return None
  2267.         
  2268.         controller.selection.selectTabByObject(myFeed)
  2269.  
  2270.     
  2271.     def addGuide(self, url = None, selected = '1'):
  2272.         
  2273.         def doAdd(url):
  2274.             db.confirmDBThread()
  2275.             myGuide = guide.getGuideByURL(url)
  2276.             if myGuide is None:
  2277.                 myGuide = guide.ChannelGuide(url)
  2278.             
  2279.             if selected == '1':
  2280.                 controller.selection.selectTabByObject(myGuide)
  2281.             
  2282.  
  2283.         self.addURL(Template(_('$shortAppName - Add Miro Guide')).substitute(shortAppName = config.get(prefs.SHORT_APP_NAME)), _('Enter the URL of the Miro Guide to add'), doAdd, url)
  2284.  
  2285.     
  2286.     def addDownload(self, url = None):
  2287.         
  2288.         def doAdd(url):
  2289.             db.confirmDBThread()
  2290.             singleclick.downloadURL(platformutils.unicodeToFilename(url))
  2291.  
  2292.         self.addURL(Template(_('$shortAppName - Download Video')).substitute(shortAppName = config.get(prefs.SHORT_APP_NAME)), _('Enter the URL of the video to download'), doAdd, url)
  2293.  
  2294.     
  2295.     def handleDrop(self, data, type, sourcedata):
  2296.         controller.handleDrop(data, type, sourcedata)
  2297.  
  2298.     
  2299.     def handleURIDrop(self, data, **kwargs):
  2300.         controller.handleURIDrop(data, **kwargs)
  2301.  
  2302.     
  2303.     def showHelp(self):
  2304.         delegate.openExternalURL(config.get(prefs.HELP_URL))
  2305.  
  2306.     
  2307.     def reportBug(self):
  2308.         delegate.openExternalURL(config.get(prefs.BUG_REPORT_URL))
  2309.  
  2310.  
  2311.  
  2312. class TemplateActionHandler:
  2313.     
  2314.     def __init__(self, display, templateHandle):
  2315.         self.display = display
  2316.         self.templateHandle = templateHandle
  2317.         self.currentName = None
  2318.  
  2319.     
  2320.     def switchTemplate(self, name, state = 'default', baseURL = None, *args, **kargs):
  2321.         self.templateHandle.unlinkTemplate()
  2322.         template = TemplateDisplay(name, state, frameHint = controller.frame, areaHint = controller.frame.mainDisplay, baseURL = baseURL, *args, **kargs)
  2323.         controller.frame.selectDisplay(template, controller.frame.mainDisplay)
  2324.         self.currentName = name
  2325.  
  2326.     
  2327.     def setViewFilter(self, viewName, fieldKey, functionKey, parameter, invert):
  2328.         logging.warning('setViewFilter deprecated')
  2329.  
  2330.     
  2331.     def setViewSort(self, viewName, fieldKey, functionKey, reverse = 'false'):
  2332.         logging.warning('setViewSort deprecated')
  2333.  
  2334.     
  2335.     def setSearchString(self, searchString):
  2336.         
  2337.         try:
  2338.             self.templateHandle.getTemplateVariable('updateSearchString')(unicode(searchString))
  2339.         except KeyError:
  2340.             e = None
  2341.             logging.warning("KeyError in getTemplateVariable ('updateSearchString')")
  2342.  
  2343.  
  2344.     
  2345.     def toggleDownloadsView(self):
  2346.         
  2347.         try:
  2348.             self.templateHandle.getTemplateVariable('toggleDownloadsView')(self.templateHandle)
  2349.         except KeyError:
  2350.             e = None
  2351.             logging.warning("KeyError in getTemplateVariable ('toggleDownloadsView')")
  2352.  
  2353.  
  2354.     
  2355.     def toggleWatchableView(self):
  2356.         
  2357.         try:
  2358.             self.templateHandle.getTemplateVariable('toggleWatchableView')(self.templateHandle)
  2359.         except KeyError:
  2360.             e = None
  2361.             logging.warning("KeyError in getTemplateVariable ('toggleWatchableView')")
  2362.  
  2363.  
  2364.     
  2365.     def toggleNewItemsView(self):
  2366.         
  2367.         try:
  2368.             self.templateHandle.getTemplateVariable('toggleNewItemsView')(self.templateHandle)
  2369.         except KeyError:
  2370.             e = None
  2371.             logging.warning("KeyError in getTemplateVariable ('toggleNewItemsView')")
  2372.  
  2373.  
  2374.     
  2375.     def toggleAllItemsMode(self):
  2376.         
  2377.         try:
  2378.             self.templateHandle.getTemplateVariable('toggleAllItemsMode')(self.templateHandle)
  2379.         except KeyError:
  2380.             e = None
  2381.             logging.warning("KeyError in getTemplateVariable ('toggleAllItemsMode')")
  2382.  
  2383.  
  2384.     
  2385.     def pauseDownloads(self):
  2386.         
  2387.         try:
  2388.             view = self.templateHandle.getTemplateVariable('allDownloadingItems')
  2389.         except KeyError:
  2390.             e = None
  2391.             logging.warning("KeyError in getTemplateVariable ('allDownloadingItems') during pauseDownloads()")
  2392.             return None
  2393.  
  2394.         for item in view:
  2395.             item.pause()
  2396.         
  2397.  
  2398.     
  2399.     def resumeDownloads(self):
  2400.         
  2401.         try:
  2402.             view = self.templateHandle.getTemplateVariable('allDownloadingItems')
  2403.         except KeyError:
  2404.             e = None
  2405.             logging.warning("KeyError in getTemplateVariable ('allDownloadingItems') during resumeDownloads()")
  2406.             return None
  2407.  
  2408.         for item in view:
  2409.             item.resume()
  2410.         
  2411.  
  2412.     
  2413.     def cancelDownloads(self):
  2414.         
  2415.         try:
  2416.             view = self.templateHandle.getTemplateVariable('allDownloadingItems')
  2417.         except KeyError:
  2418.             e = None
  2419.             logging.warning("KeyError in getTemplateVariable ('allDownloadingItems') during cancelDownloads()")
  2420.             return None
  2421.  
  2422.         for item in view:
  2423.             item.expire()
  2424.         
  2425.  
  2426.     
  2427.     def playViewNamed(self, viewName, firstItemId):
  2428.         
  2429.         try:
  2430.             view = self.templateHandle.getTemplateVariable(viewName)
  2431.         except KeyError:
  2432.             e = None
  2433.             logging.warning('KeyError in getTemplateVariable (%s) during playViewNamed()' % (viewName,))
  2434.             return None
  2435.  
  2436.         controller.playView(view, firstItemId)
  2437.  
  2438.     
  2439.     def playOneItem(self, viewName, itemID):
  2440.         
  2441.         try:
  2442.             view = self.templateHandle.getTemplateVariable(viewName)
  2443.         except KeyError:
  2444.             e = None
  2445.             logging.warning('KeyError in getTemplateVariable (%s) during playOneItem()' % (viewName,))
  2446.             return None
  2447.  
  2448.         controller.playView(view, itemID, justPlayOne = True)
  2449.  
  2450.     
  2451.     def playNewVideos(self, id):
  2452.         
  2453.         try:
  2454.             obj = db.getObjectByID(int(id))
  2455.         except database.ObjectNotFoundError:
  2456.             return None
  2457.  
  2458.         
  2459.         def myUnwatchedItems(obj):
  2460.             if obj.getState() == u'newly-downloaded' and not obj.isNonVideoFile():
  2461.                 pass
  2462.             return not (obj.isContainerItem)
  2463.  
  2464.         controller.selection.selectTabByObject(obj, displayTabContent = False)
  2465.         if isinstance(obj, feed.Feed):
  2466.             feedView = views.items.filterWithIndex(indexes.itemsByFeed, obj.getID())
  2467.             view = feedView.filter(myUnwatchedItems, sortFunc = sorts.item)
  2468.             controller.playView(view)
  2469.             view.unlink()
  2470.         elif isinstance(obj, folder.ChannelFolder):
  2471.             folderView = views.items.filterWithIndex(indexes.itemsByChannelFolder, obj)
  2472.             view = folderView.filter(myUnwatchedItems, sortFunc = sorts.item)
  2473.             controller.playView(view)
  2474.             view.unlink()
  2475.         elif isinstance(obj, tabs.StaticTab):
  2476.             view = views.unwatchedItems
  2477.             controller.playView(view)
  2478.         else:
  2479.             raise TypeError("Can't get new videos for %s (type: %s)" % (obj, type(obj)))
  2480.  
  2481.     
  2482.     def playItemExternally(self, itemID):
  2483.         controller.playbackController.playItemExternally(itemID)
  2484.  
  2485.     
  2486.     def skipItem(self, itemID):
  2487.         controller.playbackController.skip(1)
  2488.  
  2489.     
  2490.     def updateLastSearchEngine(self, engine):
  2491.         (searchFeed, searchDownloadsFeed) = self._TemplateActionHandler__getSearchFeeds()
  2492.         if searchFeed is not None:
  2493.             searchFeed.lastEngine = engine
  2494.         
  2495.  
  2496.     
  2497.     def updateLastSearchQuery(self, query):
  2498.         (searchFeed, searchDownloadsFeed) = self._TemplateActionHandler__getSearchFeeds()
  2499.         if searchFeed is not None:
  2500.             searchFeed.lastQuery = query
  2501.         
  2502.  
  2503.     
  2504.     def performSearch(self, engine, query):
  2505.         util.checkU(engine)
  2506.         util.checkU(query)
  2507.         (searchFeed, searchDownloadsFeed) = self._TemplateActionHandler__getSearchFeeds()
  2508.         if searchFeed is not None and searchDownloadsFeed is not None:
  2509.             searchFeed.preserveDownloads(searchDownloadsFeed)
  2510.             searchFeed.lookup(engine, query)
  2511.         
  2512.  
  2513.     
  2514.     def resetSearch(self):
  2515.         (searchFeed, searchDownloadsFeed) = self._TemplateActionHandler__getSearchFeeds()
  2516.         if searchFeed is not None and searchDownloadsFeed is not None:
  2517.             searchFeed.preserveDownloads(searchDownloadsFeed)
  2518.             searchFeed.reset()
  2519.         
  2520.  
  2521.     
  2522.     def sortBy(self, by, section):
  2523.         
  2524.         try:
  2525.             self.templateHandle.getTemplateVariable('setSortBy')(by, section, self.templateHandle)
  2526.         except KeyError:
  2527.             e = None
  2528.             logging.warning("KeyError in getTemplateVariable ('setSortBy')")
  2529.  
  2530.  
  2531.     
  2532.     def handleSelect(self, area, viewName, id, shiftDown, ctrlDown):
  2533.         
  2534.         try:
  2535.             view = self.templateHandle.getTemplateVariable(viewName)
  2536.         except KeyError:
  2537.             e = None
  2538.             logging.warning('KeyError in getTemplateVariable (%s) during handleSelect()' % (viewName,))
  2539.             return None
  2540.  
  2541.         shift = shiftDown == '1'
  2542.         ctrl = ctrlDown == '1'
  2543.         controller.selection.selectItem(area, view, int(id), shift, ctrl)
  2544.  
  2545.     
  2546.     def handleContextMenuSelect(self, id, area, viewName):
  2547.         
  2548.         try:
  2549.             obj = db.getObjectByID(int(id))
  2550.         except:
  2551.             traceback.print_exc()
  2552.  
  2553.         
  2554.         try:
  2555.             view = self.templateHandle.getTemplateVariable(viewName)
  2556.         except KeyError:
  2557.             e = None
  2558.             logging.warning('KeyError in getTemplateVariable (%s) during handleContextMenuSelect()' % (viewName,))
  2559.             return None
  2560.  
  2561.         if not controller.selection.isSelected(area, view, int(id)):
  2562.             self.handleSelect(area, viewName, id, False, False)
  2563.         
  2564.         popup = menu.makeContextMenu(self.currentName, view, controller.selection.getSelectionForArea(area), int(id))
  2565.         if popup:
  2566.             delegate.showContextMenu(popup)
  2567.         
  2568.  
  2569.     
  2570.     def __getSearchFeeds(self):
  2571.         searchFeed = controller.getGlobalFeed('dtv:search')
  2572.         if not searchFeed is not None:
  2573.             raise AssertionError
  2574.         searchDownloadsFeed = controller.getGlobalFeed('dtv:searchDownloads')
  2575.         if not searchDownloadsFeed is not None:
  2576.             raise AssertionError
  2577.         return (searchFeed, searchDownloadsFeed)
  2578.  
  2579.     
  2580.     def setVolume(self, level):
  2581.         pass
  2582.  
  2583.     
  2584.     def setVideoProgress(self, pos):
  2585.         pass
  2586.  
  2587.  
  2588.  
  2589. def stringToBoolean(string):
  2590.     if string == '' and string == '0' or string == 'false':
  2591.         return False
  2592.     else:
  2593.         return True
  2594.  
  2595.  
  2596. class Playlist:
  2597.     
  2598.     def __init__(self, view, firstItemId):
  2599.         self.initialView = view
  2600.         self.filteredView = self.initialView.filter(mappableToPlaylistItem)
  2601.         self.view = self.filteredView.map(mapToPlaylistItem)
  2602.         self.view.confirmDBThread()
  2603.         self.view.resetCursor()
  2604.         while True:
  2605.             cur = self.view.getNext()
  2606.             if cur == None:
  2607.                 self.view.resetCursor()
  2608.                 self.view.getNext()
  2609.                 break
  2610.             
  2611.             if firstItemId is None or cur.getID() == int(firstItemId):
  2612.                 break
  2613.                 continue
  2614.  
  2615.     
  2616.     def reset(self):
  2617.         self.initialView.removeView(self.filteredView)
  2618.         self.initialView = None
  2619.         self.filteredView = None
  2620.         self.view = None
  2621.  
  2622.     
  2623.     def cur(self):
  2624.         return self.itemMarkedAsViewed(self.view.cur())
  2625.  
  2626.     
  2627.     def getNext(self):
  2628.         return self.itemMarkedAsViewed(self.view.getNext())
  2629.  
  2630.     
  2631.     def getPrev(self):
  2632.         return self.itemMarkedAsViewed(self.view.getPrev())
  2633.  
  2634.     
  2635.     def itemMarkedAsViewed(self, anItem):
  2636.         if anItem is not None:
  2637.             eventloop.addIdle(anItem.onViewed, 'Mark item viewed')
  2638.         
  2639.         return anItem
  2640.  
  2641.  
  2642.  
  2643. class PlaylistItemFromItem:
  2644.     
  2645.     def __init__(self, anItem):
  2646.         self.item = anItem
  2647.         self.dcOnViewed = None
  2648.  
  2649.     
  2650.     def getTitle(self):
  2651.         return self.item.getTitle()
  2652.  
  2653.     
  2654.     def getVideoFilename(self):
  2655.         return self.item.getVideoFilename()
  2656.  
  2657.     
  2658.     def getLength(self):
  2659.         return 42.42
  2660.  
  2661.     
  2662.     def onViewedExecute(self):
  2663.         if self.item.idExists():
  2664.             self.item.markItemSeen()
  2665.         
  2666.         self.dcOnViewed = None
  2667.  
  2668.     
  2669.     def onViewed(self):
  2670.         if self.dcOnViewed or self.item.getSeen():
  2671.             return None
  2672.         
  2673.         self.dcOnViewed = eventloop.addTimeout(5, self.onViewedExecute, 'Mark item viewed')
  2674.  
  2675.     
  2676.     def onViewedCancel(self):
  2677.         if self.dcOnViewed:
  2678.             self.dcOnViewed.cancel()
  2679.             self.dcOnViewed = None
  2680.         
  2681.  
  2682.     
  2683.     def getID(self):
  2684.         return self.item.getID()
  2685.  
  2686.     
  2687.     def __getattr__(self, attr):
  2688.         return getattr(self.item, attr)
  2689.  
  2690.  
  2691.  
  2692. def mappableToPlaylistItem(obj):
  2693.     if isinstance(obj, item.Item):
  2694.         pass
  2695.     return obj.isDownloaded()
  2696.  
  2697.  
  2698. def mapToPlaylistItem(obj):
  2699.     return PlaylistItemFromItem(obj)
  2700.  
  2701.  
  2702. def _getThemeHistory():
  2703.     if len(views.themeHistories) > 0:
  2704.         th = views.themeHistories[0]
  2705.         th.checkNewTheme()
  2706.         return th
  2707.     else:
  2708.         return theme.ThemeHistory()
  2709.  
  2710.  
  2711. def _getInitialChannelGuide():
  2712.     default_guide = None
  2713.     newGuide = False
  2714.     for guideObj in views.guides:
  2715.         if guideObj.getDefault():
  2716.             default_guide = guideObj
  2717.             break
  2718.             continue
  2719.     
  2720.     if default_guide is None:
  2721.         newGuide = True
  2722.         logging.info('Spawning Miro Guide...')
  2723.         default_guide = guide.ChannelGuide(config.get(prefs.CHANNEL_GUIDE_URL), config.get(prefs.CHANNEL_GUIDE_ALLOWED_URLS).split())
  2724.         initialFeeds = resources.path('initial-feeds.democracy')
  2725.         if os.path.exists(initialFeeds):
  2726.             urls = subscription.parseFile(initialFeeds)
  2727.             if urls is not None:
  2728.                 for url in urls:
  2729.                     feed.Feed(url, initiallyAutoDownloadable = False)
  2730.                 
  2731.             
  2732.             dialog = dialogs.MessageBoxDialog(_('Custom Channels'), Template(_('You are running a version of $longAppName with a custom set of channels.')).substitute(longAppName = config.get(prefs.LONG_APP_NAME)))
  2733.             dialog.run()
  2734.             controller.initial_feeds = True
  2735.         
  2736.     
  2737.     return (newGuide, default_guide)
  2738.  
  2739.  
  2740. def changeMoviesDirectory(newDir, migrate):
  2741.     if not util.directoryWritable(newDir):
  2742.         dialog = dialogs.MessageBoxDialog(_('Error Changing Movies Directory'), _("You don't have permission to write to the directory you selected.  Miro will continue to use the old videos directory."))
  2743.         dialog.run()
  2744.         return None
  2745.     
  2746.     oldDir = config.get(prefs.MOVIES_DIRECTORY)
  2747.     config.set(prefs.MOVIES_DIRECTORY, newDir)
  2748.     if migrate:
  2749.         views.remoteDownloads.confirmDBThread()
  2750.         for download in views.remoteDownloads:
  2751.             if download.isFinished():
  2752.                 logging.info('migrating %s', download.getFilename())
  2753.                 download.migrate(newDir)
  2754.                 continue
  2755.         
  2756.         
  2757.         try:
  2758.             os.rmdir(os.path.join(oldDir, 'Incomplete Downloads'))
  2759.         except:
  2760.             pass
  2761.  
  2762.         
  2763.         try:
  2764.             os.rmdir(oldDir)
  2765.  
  2766.     
  2767.     util.getSingletonDDBObject(views.directoryFeed).update()
  2768.  
  2769. changeMoviesDirectory = eventloop.asUrgent(changeMoviesDirectory)
  2770.  
  2771. def saveVideo(currentPath, savePath):
  2772.     logging.info('saving video %s to %s' % (currentPath, savePath))
  2773.     
  2774.     try:
  2775.         shutil.copyfile(currentPath, savePath)
  2776.     except:
  2777.         title = _('Error Saving Video')
  2778.         name = os.path.basename(currentPath)
  2779.         text = _('An error occured while trying to save %s.  Please check that the file has not been deleted and try again.') % util.clampText(name, 50)
  2780.         dialogs.MessageBoxDialog(title, text).run()
  2781.         logging.warn('Error saving video: %s' % traceback.format_exc())
  2782.  
  2783.  
  2784. saveVideo = eventloop.asUrgent(saveVideo)
  2785.